I was wondering what is the best thing to do here. I was given an assignment and I have to update a Date class that uses Ctime.
The class must now work for date before 1970. I was looking around and I didn't find much...
So I guess my question is what would be the best way to achieve that?
Date Range from [1970-2037] to [1900-2037]
Assuming you mean CTime from MFC, if you enable OLE you can use COleDateTime as a drop-in replacement. Per the documentation, it supports dates "from January 1, 100, through December 31, 9999".
You could store the date as a signed integer number of days since 1970-01-01 or any other epoch. Negative dates would be the date before 1970.
Or you could use Boost.Date_Time library.
How about a date class that stores an internal std::tm datetime object and the total seconds since (or before) Jan 1, 1970 as a time_t. The members of tm are all ints and time_t should be 64bit everywhere (I hope), so in theory that should cover all the times you could consider.
In the constructor of your class you'd need to compute those total seconds and the only standard library function that seems to do that is mktime. Unfortunately this one only works for dates after Jan 1, 1970.
One possible workaround... add a really big number to the year and work with that internally.
#include <ctime>
#include <iostream>
class CustomDateTime {
const int MKTIME_DELTA = 100000;
std::tm _datetime;
std::time_t _total_seconds;
public:
CustomDateTime(int year, int mon, int day, int hour, int min, int sec) {
_datetime.tm_year = year - 1900 + MKTIME_DELTA;
_datetime.tm_mon = mon - 1;
// copy day, hour, min, sec
_total_seconds = std::mktime(&_datetime);
}
bool operator==(const CustomDateTime& rhs) {
return _total_seconds == rhs._total_seconds;
}
void print() {
std::cout << _datetime.tm_year + 1900 - MKTIME_DELTA << ':'
<< _datetime.tm_mon + 1 << _datetime.tm_mday << '\n';
}
};
That should cover all years between 1970 - MKTIME_DELTA = -98030 and the far far future.
Related
TL;DR: How to use a std::chrono::system_clock::time_point to compare based on only certain parameters (e.g. I just want hours, minutes and seconds, but not day, month, etc.).
Also: After converting the std::chrono::system_clock::time_point to a std::tm, the std::tm.tm_hours contains a value one higher than originally input to the std::chrono::system_clock::time_point.
My theoretical approach on getting a std::chrono::system_clock::time_point to work:
typedef std::chrono::system_clock::time_point TimePoint;
TimePoint MainWindow::createTimePoint(int h, int m)
{
TimePoint createdTime = std::chrono::system_clock::time_point{std::chrono::hours(h) + std::chrono::minutes(m)};
time_t tt = std::chrono::system_clock::to_time_t(createdTime);
tm timeExtracted = *localtime(&tt);
std::cout << "input:\t\t" << "H = " << h << ", M = " << m << std::endl;
std::cout << "timeExtracted:\t" << "H = " << timeExtracted.tm_hour << ", M = " << timeExtracted.tm_min << std::endl;
return createdTime;
}
If I run this, the hours of timeExtracted are always +1 from the input h.
Why is that so? And how to fix this? I went over a few other posts that showed this, but they couldnt help me. Probably also because of this:
I think that when I create a TimePoint, the day, month, etc. is also set to a random value or initiated to a certain value. The point is: I want them to always be the same value, so that my TimePoint (after converting) basically shows this:
timeExtracted.tm_sec = 0
timeExtracted.tm_min = m
timeExtracted.tm_hour = h
timeExtracted.tm_mon = 0
timeExtracted.tm_wday = 0
timeExtracted.tm_mday = 0
timeExtracted.tm_yday = 0
timeExtracted.tm_year = 0
timeExtracted.tm_isdst = 0
How can I compare two of these TimePoint utilising using the compare operations of std::chrono on them, but only compare the hour and minute.
If my question is unclear, I'm sorry, it's late in the evening. I'll check again next morning. Thank you.
I'm going to start an answer, but this isn't going to be a complete answer because I'm not yet sure of the complete question. However, I can help.
TimePoint createdTime = system_clock::time_point{hours(h) + minutes(m)};
(I've clipped the std::chrono:: qualifiers so that this is easier to read and discuss)
This creates a time stamp that is 1970-01-01 hh:mm:00 UTC. In a nutshell, system_clock::time_point is measuring the duration of time (in some units like microseconds or nanoseconds) since New Years 1970, UTC. Technically the above is an approximation, system_clock doesn't count leap seconds, but we can (and should) ignore that detail for now.
This:
tm timeExtracted = *localtime(&tt);
is going to introduce UTC offset corrections based on your computer's setting for the local time zone. The time zone adjustment rules are (hopefully) going to be based on what was in effect in 1970 in your area.
There exist techniques and libraries for taking a system_clock::time_point and breaking it up into fields such as {year, month, day, hours, minutes, seconds, microseconds}. But that conversion also depends on if you want these fields in UTC, local time, or some other arbitrary time zone.
And the very first step is to apply the UTC offset associated with some time zone if desired. It may be that your {h, m} input needs a UTC offset adjustment prior to putting them into system_clock::time_point if the intent is that {h, m} represent local time instead of UTC.
Update: Store hours example
This example will use my free, open-source time zone library, because I feel it is much easier to work with and allows for more readable and expressive code.
This example takes as input a system_clock::time_point and compares it to a list of open/close times for each day of the week and determines if the input time is inside or outside of those time-of-day ranges for the weekday associated with the input time t. The store hours are presumed to be stated with respect to the store's local time zone, which is also the current time zone set for the computer running this code.
#include "date/tz.h"
#include <algorithm>
#include <cassert>
#include <chrono>
bool
is_store_open_at(std::chrono::system_clock::time_point tp)
{
using namespace date;
using namespace std::chrono;
struct day_schedule
{
weekday wd;
minutes open;
minutes close;
};
// hours are expressed in terms of local time
static constexpr day_schedule store_hours[]
{
// week day open-time close-time
{Monday, 0h, 0h}, // closed all day
{Tuesday, 8h, 18h},
{Wednesday, 8h, 18h},
{Thursday, 8h, 18h},
{Friday, 8h, 18h},
{Saturday, 8h, 15h+30min},
{Sunday, 9h+30min, 15h}
};
auto local_tp = current_zone()->to_local(tp);
auto local_day = floor<days>(local_tp);
auto local_time_of_day = local_tp - local_day;
weekday local_weekday{local_day};
auto ds = std::find_if(std::begin(store_hours), std::end(store_hours),
[local_weekday](day_schedule const& x)
{
return x.wd == local_weekday;
});
assert(ds != std::end(store_hours));
return ds->open <= local_time_of_day && local_time_of_day < ds->close;
}
#include <iostream>
int
main()
{
std::cout << is_store_open_at(std::chrono::system_clock::now()) << '\n';
}
The function begins by defining some handy data structures to store the open and close times for each day of the week. The open and close members of day_schedule measure "minutes since midnight" in local time.
The input time tp is in terms of UTC, since its type is system_clock::time_point. This is not currently specified by the C++ standard, but will be for next year's C++20.
zoned_seconds is used to convert the UTC time t into local time according to the computers time zone setting obtained by calling current_zone(). I've truncated t to seconds to simplify some of the syntax. This isn't strictly necessary. I've edited to use slightly simpler syntax to eliminate the zoned_seconds. zoned_seconds can be really useful in other examples, but in this one was more trouble than it was worth. auto local_tp = current_zone()->to_local(tp) is a simpler way to translate UTC to a local time point.
local_tp is a chrono::time_point that is considered "local time", and is distinct from the family of chrono::time_points associated with system_clock. The advantage of doing this is so that if local time and UTC time are accidentally mixed, it is a compile-time error.
local_days is simply local_tp truncated to days precision. It is still a chrono::time_point, just a coarse one that points to the beginning of the day as described by the local time zone.
The time duration since the local midnight is simply local_tp - local_day.
The day of the week (as defined by the local time zone) can be obtained by converting local_day to type weekday. This is the local day of the week associated with tp.
Now it is a simple matter to search store_hours for the entry that matches local_weekday.
The store is open if local_time_of_day is at or past the open time and has not yet reached the close time.
If the "store hours" are specified in UTC instead of local time, then this program simplifies somewhat, but is still similar.
I want to calculate past date from today based on Months input.
Like today is 29-08-2019, 6 months before it was be 29-02-2019.
User inputs would be number of months. It could be 6, 8, 18, 30, 60....
I want to calculate exact complete date. I have tried below code which helps me to get Date for current and past year, but I am looking some solution to get date for months value which is much higher.
time_t now = time( NULL);
struct tm now_tm = *localtime( &now);
int inDuration = 0;
std::cout << "Add Duration..." << std::endl;
std::cin >> inDuration; //month value. looking for solution when mnth value is more then month in current and previous year.
int crnMonth = now_tm.tm_mon+1;
int pastDay = now_tm.tm_mday;
int pastMonth = 0;
int pastYear = now_tm.tm_year + 1900;
if(inDuration > crnMonth)
{
pastMonth = (12-(inDuration-crnMonth));
pastYear = (now_tm.tm_year + 1900)-1;
}
else
{
pastMonth = crnMonth-inDuration;
}
printf("%d-%d-%d", pastDay, pastMonth, pastYear);
This is very easy using Howard Hinnant's free, open-source date/time library:
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
// Get current local date
auto now_utc = floor<seconds>(system_clock::now());
auto now_local = zoned_seconds{current_zone(), now_utc}.get_local_time();
year_month_day crnDate{floor<days>(now_local)};
// Get number of months
int inDuration = 0;
std::cout << "Add Duration..." << std::endl;
std::cin >> inDuration;
// Compute past date
auto pastDate = crnDate - months{inDuration};
if (!pastDate.ok())
pastDate = pastDate.year()/pastDate.month()/last;
std::cout << pastDate << '\n';
}
Example output:
35
2016-09-29
The first few lines get the current time (in UTC), and then convert that to your local time, and then converts the local time to a {year, month, day} structure.
Then the number of desired months is obtained from the user as in your question.
Finally, the input is converted to a months duration and that is subtracted from the current date. If the current date is near the end of a month, it is possible that the past date won't be a valid date (e.g. September 31). If this happens, one has to decide on a policy of what to do. The above example chooses to "snap" to the end of the month. Other policies such as overflowing into the next month are also possible.
Some installation is required for the local time zone support.
If getting the current date in UTC is good enough, then no time zone support is required and you could use the header-only library "date/date.h" (requires no installation). Just #include "date/date.h" instead of #include "date/tz.h", and change the first 3 lines to:
// Get current UTC date
year_month_day crnDate{floor<days>(system_clock::now())};
And the rest of the program works with the same code.
In some multimedia metadata, there may be date-time in seconds since
midnight, Jan. 1, 1904, in UTC time.
As I know, the date time function is normally based on 1970-1-1 midnight in C/C++ standard library, at least in Visual C++, is there a function in C/C++/Win32-API to convert the seconds since "1904-1-1 midnight" to a date time string, like as "hh:mm:ss MM. dd, yyyy" or other format string or a structure like as "struct tm"?
struct tm
{
int tm_sec; // seconds after the minute - [0, 60] including leap second
int tm_min; // minutes after the hour - [0, 59]
int tm_hour; // hours since midnight - [0, 23]
int tm_mday; // day of the month - [1, 31]
int tm_mon; // months since January - [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday - [0, 6]
int tm_yday; // days since January 1 - [0, 365]
int tm_isdst; // daylight savings time flag
};
Solution#1:
int main()
{
SYSTEMTIME systm;
memset(&systm, 0, sizeof(systm));
systm.wYear = 1904;
systm.wMonth = 1;
systm.wDay = 1;
FILETIME filetm;
if (SystemTimeToFileTime(&systm, &filetm) == FALSE){
printf("Failed to convert system time to file-time.\n");
return 0;
}
ULARGE_INTEGER nanoSeconds;
nanoSeconds.HighPart = filetm.dwHighDateTime;
nanoSeconds.LowPart = filetm.dwLowDateTime;
nanoSeconds.QuadPart += 3600ULL * 10000000; // add 1hour based on 1904/1/1 midnight
filetm.dwHighDateTime = nanoSeconds.HighPart;
filetm.dwLowDateTime = nanoSeconds.LowPart;
if (FileTimeToSystemTime(&filetm, &systm) == FALSE){
printf("Failed to convert file-time to system time.\n");
return 0;
}
printf("New system time by adding 1 hour: %d-%02d-%02d %02d:%02d:%02d.%03d\n",
systm.wYear, systm.wMonth, systm.wDay,
systm.wHour, systm.wMinute, systm.wSecond, systm.wMilliseconds);
return 0;
}
The output is
New system time by adding 1 hour: 1904-01-01 01:00:00.000
Solution#2:
With #Howard Hinnant's date.h, it can also solve this issue, please see the sample code provided by him https://stackoverflow.com/a/49733937/3968307
This would be a good time to use Howard Hinnant's free, open-source date/time library:
#include "date/date.h"
#include <cstdint>
#include <iostream>
#include <string>
std::string
convert(std::int64_t seconds_since_1904)
{
using namespace date;
using namespace std::chrono;
constexpr auto offset = sys_days{January/1/1970} - sys_days{January/1/1904};
return format("%T %m.%d, %Y", sys_seconds{seconds{seconds_since_1904}} - offset);
}
int
main()
{
std::cout << convert(3'606'124'378) << '\n';
}
Output:
13:12:58 04.09, 2018
Update
The above code will port to C++20 (when it ships) by:
Change #include "date/date.h" to #include <chrono>
Change using namespace date; to using namespace std;
Change "%T %m.%d, %Y" to "{:%T %m.%d, %Y}"
As you can easily calculate with any available spreadsheet application in your system, the difference in seconds between those two timestamps (assumed both are in UTC time) the difference in seconds from 1/1/1904 to 1/1/1970 is 2,082,844,800 sec. So the conversion function from a unix timestamp to your time, consists in adding 2082844800 to the unix timestamp you receive from any of the time functions. In case you want to pass back from a timestamp in your time to unix timestamp, then subtract that fixed value from your timescale. Beware that that number does not fit in a signed int so you must use probably a 64bit number to manage properly all those timestamps. Worse if you want to use nanoseconds resolution.
I don't guess the reason of using that strange epoch timestamp, but to illustrate a practical and in use application of such differences, there's a timestamp in internet that uses an epoch close to that, that is the NTP (Network Time Protocol) timestamp, that is based on 1/1/1900 epoch and has a resolution of 1/2**32 sec. that is around 232 ps. for a specification of this protocol, see RFC-5905
The time problem here practically begs you to write your own code for it. The year 1900 is an exception since it is divisible by 4 but still is not a leap year, so by starting in 1904 you can avoid that particular exception and use the fact that there are 1461 days in every four-year period starting with 1904.
I am converting CLI C++ code to standard C++, and i have a piece of code that gets a UINT64 number (from a remote server - so i can't change to format/precision of the time i get) and converts it into DateTime object and later outputs the following value: myDatetime.ToString("dd/MM/yyyy hh:mm:ss.fffffff tt").
I haven't found a way to convert unsigned int 64 into time in C++.
The following code does nothing for numbers so big (that's the 64bit number i get from the server).
time_t rawtime=131274907755873979
localtime_s(&timeinfo, &rawtime);
I need some help :)
My question wan't answered in the thread Convert Epoch Time string to Time since it doesn't work for numbers as large as i need. For example the number 131274907755873979 which is what i get from the server. The function ctime for that value simply returns NULL.
I need a way to convert between the time i get as a unsigned int64 into standard C++ time object.
std::string LongToString(int64_t longDate) {
char buff[128];
std::chrono::duration<int64_t, std::milli> dur(longDate);
auto tp = std::chrono::system_clock::time_point(
std::chrono::duration_cast<std::chrono::system_clock::duration>(dur));
std::time_t in_time_t = std::chrono::system_clock::to_time_t(tp);
strftime(buff, 128, "%Y-%m-%d %H:%M:%S", localtime(&in_time_t));
std::string resDate(buff);
return resDate;
}
This is a case with bsoncxx::types::b_date get_date().to_int64() MongoDB.
The DateTime saved with int64_t.
You have not told us how the existing code converts that number into a DateTime. Let us suppose that it does so by invoking this constructor: DateTime( long long ticks ).
According to the documentation of that constructor of DateTime,
long long ticks A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
On the other hand, according to the documentation of localtime_s and the documentation of time_t, localtime_s() requires
the number of seconds (not counting leap seconds) since 00:00, Jan 1 1970 UTC.
So, you first need to convert 100-nanosecond intervals to seconds, and then convert from January 1, 0001 to January 1, 1970.
Using Howard Hinnant's datetime library this computation can be done quite easily. It works with VS 2013 and later.
#include "tz.h"
#include <cstdint>
#include <string>
#include <iostream>
std::string
FILETIME_to_string(std::uint64_t i)
{
using namespace std;
using namespace std::chrono;
using namespace date;
using FileTime = duration<int64_t, ratio<1, 10000000>>;
auto const offset = sys_days{jan/1/1970} - sys_days{jan/1/1601};
auto tp = sys_days{jan/1/1970} + (FileTime{static_cast<int64_t>(i)} - offset);
return format("%d/%m/%Y %I:%M:%S %p", make_zoned("Etc/GMT-2", tp));
}
int
main()
{
std::cout << FILETIME_to_string(131274907755873979) << '\n';
}
This skips DateTime and goes straight to the string. I wasn't sure what you are wanting with tt in the format. But whatever it is, it can be handled.
This library builds on the C++11 <chrono> library. So the first thing to do is to create a duration to represent the windows tick size (100 ns). Then just compute the offset between the two epochs and subtract it from the input, and form a std::chrono::time_point. Now you can format that time_point however you want.
The program above outputs:
29/12/2016 03:12:55.5873979 PM
If you use VS 2017 you'll be able to make offset constexpr, making the conversion more efficient.
Whats the best way to compare dates in c++ which are made into a structure
that has a day,month and year variable i.e how to check that 30 days have passed since a event and so on. I personally found that when comparing 2 dates in the same year I make both dates into "days-dates" using a array that holds the day count of all the months of the year and then convert the dates into days and do the calculations I need. Is there a easier way to compare the dates ?
Here is a very good C++11/14 library for handling dates1. It allows you to have {year, month, day} structures (called date::year_month_day), and {count-of-days} structures (called sys_days). It also allows easy and efficient conversions between these two structures. And naturally there are comparison operators.
The entire library is essentially an extension of <chrono> into calendar types.
Video introduction found here:
https://www.youtube.com/watch?v=tzyGjOm8AKo
Lots of example code here.
Here is a detailed explanation of the underlying algorithms to convert between the {year, month, day} structure and the {count-of-days} structure:
http://howardhinnant.github.io/date_algorithms.html
Here are some date creation and comparison examples for both field (year_month_day) and serial (sys_days) data structures:
#include "date.h"
int
main()
{
using namespace date::literals;
// create and compare {year, month, day} structures
constexpr auto ymd1 = 2017_y/jan/21;
constexpr auto ymd2 = ymd1 + date::months{15};
static_assert(ymd2 > ymd1, "ymd2 is 15 months after ymd1");
static_assert(ymd2 == 2018_y/apr/21, "ymd2 is 2018-04-21");
// create and compare {count_of_days} structures
constexpr date::sys_days sd2 = ymd2;
static_assert(sd2 == ymd2, "sd2 is the same day as ymd2");
static_assert(sd2.time_since_epoch().count() == 17642, "sd2 is day 17642");
constexpr date::sys_days sd1 = sd2 - date::days{465};
static_assert(sd1 < sd2, "sd1 is 465 days before sd2");
static_assert(sd1.time_since_epoch().count() == 17177, "sd1 is day 17177");
static_assert(sd1 == 2017_y/jan/11, "sd1 is 2017-01-11");
}
The constexpr / static_assert requires a fully conforming C++14 compiler. For C++11, remove the constexpr and change static_assert to assert (and eliminate the static_assert message).
date::sys_days is a typedef for the chrono::time_point:
time_point<system_clock, duration<int, ratio<86400>>
The above example code requires only "date.h", and no other C++ source files (no installation). There is also available a timezone library at this same github location, but that does require some installation.
1 I am the principal author of this library.
For what you describe, the standard C style routines are probably going to work best: http://en.cppreference.com/w/cpp/chrono/c
Here is a rough example using C style coding
#include <ctime>
const int SECONDS_PER_DAY = 24 * 60 * 60;
tm day1;
memset( &day1, 0, sizeof(day1) );
day1.tm_year = 2016 - 1900; // tm_year is the years since 1900
// so to represent 2016, the value is 116.
day1.tm_mon = 0;
day1.tm_mday = 20;
time_t day1a = mktime( &day1 );
tm day2;
memset( &day2, 0, sizeof(day2) );
day1.tm_year = 2008 - 1900;
day1.tm_mon = 0;
day1.tm_mday = 20;
time_t day2a = mktime( &day2 );
double day_delta = difftime( day1, day2 ); // returns number of seconds
double days_past = (day_delta / SECONDS_PER_DAY)
This is standard and will work on everywhere.
N.B. the POSIX standard defines time_t as an integral type
If you need to implement it yourself then simplest way is to keep date as a single integer - Julian day As you can see on the article calculation from month/day/year into Julian day and back is pretty trivial (from computer perspective of course). When you keep dates as a single number calculate difference in days or compare them is trivial. Or if you say that it is already implemented as a structure you can convert your dates into Julian day on the fly (or keep mutable field to cache that value).