non-integral difference in days - moment-timezone

I'm not asking why the following differenceInDays is wrong.
After figuring that out I kind of kicked myself.
But I wonder what's the right fix.
/**
* differenceInDays - return the difference in days between 2 dates.
*
* Since `moment.duration.asDays` can return a non-integral value
* (i.e. 36 hours == 1.5 days) each date is first adjusted to the
* start of day before the difference is determined.
*/
const differenceInDays = (date1, date2, timezone) => {
const localStartOfDay1 = moment(date1)
.tz(timezone)
.startOf('day');
const localStartOfDay2 = moment(date2)
.tz(timezone)
.startOf('day');
return moment.duration(localStartOfDay2.diff(localStartOfDay1)).asDays();
};
expect(differenceInDays('2020-03-08T17:00:00Z',
'2020-03-09T17:00:00Z',
'America/New_York')).toEqual(1);
Expected value to equal:
1
Received:
0.9583333333333334

The result might not be integral if DST transition or a leap adjustment happens between date1 and date2.
One fix could be to just round the result.

Related

I try to divide two functions from each other and get 0 every time

I have the code underneat in Amazone Athena to calculate how the last 7 days in footfall compare to the average week last year.
I only get a 0 as a result all the time, what is the problem.
I tried to make the x and y as Float but that still gave zero's
The data is on daily basis and a calculate a week average of last year by addding all weeks and divide by 52 (probably also a better way to do this)
select x.visitors/y.visitors*100
from
(select sum(visitors) as visitors
from corrected_scanners_per_day
where btcode in ('BT120031', 'BT120000','BT902', 'BT120052', 'BT120050', 'BT130109', 'BT120131', 'BT130110', 'BT130107', 'BT120126', 'BT120078', 'BT120076', 'BT120035', 'BT130450', 'BT120063', 'BT120044', 'BT120082', 'BT120030', 'BT120116', 'BT121196', 'BT130366', 'BT120085', 'BT120053', 'BT120014')
And datetime
BETWEEN current_date - interval '7'day
AND current_date) x
Join
(select sum(visitors) / 52 as visitors, year(datetime) as year
from corrected_scanners_per_day
where btcode in ('BT120031', 'BT120000','BT902', 'BT120052', 'BT120050', 'BT130109', 'BT120131', 'BT130110', 'BT130107', 'BT120126', 'BT120078', 'BT120076', 'BT120035', 'BT130450', 'BT120063', 'BT120044', 'BT120082', 'BT120030', 'BT120116', 'BT121196', 'BT130366', 'BT120085', 'BT120053', 'BT120014')
And datetime
BETWEEN CAST('2019-01-01' AS timestamp)
AND CAST('2019-12-31' AS timestamp)
group by year(datetime)
order by year(datetime)) y on 1=1
This is happening because your data types (visitors and 100) are all INT, so your output data type will also be an INT. When the calculation is converted to an INT, it is probably rounding to 0. So you need to explicitly make sure your final data type is a numeric type that allows decimals.
Try changing the first line to this:
select (x.visitors * 1.0) / (y.visitors * 1.0) * 100.0
You have other INT types inside your subqueries that should also probably be converted to decimals to make sure no truncation or rounding is occurring.

Solution doesn't work for number of days between two dates

I know this question has been asked a few times, and I'm asking again because I've got issues with existing solutions on SO.
My goal is to find number of days between 1900-01-01 and a given date. The date will be in the format as yyyy-mm-dd and the type is std::string.
The solution I've followed is https://stackoverflow.com/a/14219008/2633803
And below is my version:
std::string numberOfDaysSince1900v2(std::string aDate)
{
string year, month, day;
year = aDate.substr(0, 4);
month = aDate.substr(5, 2);
day = aDate.substr(8, 2);
struct std::tm a = { 0,0,0,1,1,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month),std::stoi(year) - 1900 };
std::time_t x = std::mktime(&a);
std::time_t y = std::mktime(&b);
double difference;
if (x != (std::time_t)(-1) && y != (std::time_t)(-1))
{
difference = std::difftime(y, x) / (60 * 60 * 24) + 36526; //36526 is number of days between 1900-01-01 and 2000-01-01
}
return std::to_string(difference);
}
It worked fine until the given date comes to 2019-01-29 and 2019-02-01. In both cases, the output is 43494. And for the whole Feb, the output is 3 days less than expected. Then, when it comes to March 2019, the output is back to normal again.
Another case is 2019-09-03, the output is 43710, whilst the expected output is 43711.
Why would this happen to these specific dates? I ran the solution step by step and closely watched the variables in the memory but couldn't explain it.
Any suggestion is appreciated. Thanks.
The month should be represented as an integer between 0 and 11, not 1 and 12.
So
struct std::tm a = { 0,0,0,1,0,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month)-1,std::stoi(year) - 1900 };
I would say there are other problems with your code. You cannot reliably initialise a tm like that (the order of fields within the struct is not guaranteed). Neither does difftime necessarily return a number of seconds (which you are assuming).

Find the difference between the dates and group it under some category

I have a start date and an end date. I need to find the difference between these dates and group it under the following categories.
< 1 year, < 2 year and so on till X years.
I'm trying to write a unix C++ program for this problem.
I can easily find the unix time difference between start and end date and compare with the 1 year's time stamp (12 * 30 * 20 * 60 * 60) and so on.
Is there any C++ function that returns the difference in years given the start and end date? Also let's say, the difference is 8 years, I suppose I have to write conditions like this,
if((end_date - start_date) < 12 * 30 * 24 * 60 * 60)
group = " less than 1 year"
...
...
Until what point do I stop at, as I won't know what the maximum difference is between the dates?
Is there any easy way to compute this?
I know i'm confusing here, but i ve put all my efforts to explain the problem here. Thanks in advance.
Also note, this is not a assignment or anything.
Assuming "precise years" (in other words, all years are 365 days long) is not an issue, I would do something like this (counting the number of times each year happens in this case - since the original question doesn't really say WHAT to do with each year)
const int MAX_YEARS = 10;
const int YEAR_IN_SECONDS = 365 * 24 * 60 * 60;
std::array<int, MAX_YEARS+1> bins;
int years = static_cast<int>(difftime(end_date - start_date) / YEAR_IN_SECONDS);
// Outside of range, put it at the end of range...
// We could discard or do something else in this case.
if (years > MAX_YEARS)
{
years = MAX_YEARS;
}
bins[years]++; // Seen one more of "this year".
Obviously, what you do with "bins", and what/how you store data there really depends on what you actually are trying to achieve.
An alternative solution would be to use const double YEAR_IN_SECONDS = 365.25 * 24 * 60 * 60;, which would slightly better cover for leap-years. If you want to be precise about it, you'd have to find out if you are before or after each of the leapday in a particular year that is divisible by 4 (and keep in mind that there are special cases for years divisible by 100 and other rules at 400).
#include <chrono>
using years = std::chrono::duration<std::chrono::system_clock::rep, std::ratio<365 * 24 * 60 * 60, 1>>;
std::chrono::system_clock::time_point end_date = std::chrono::system_clock::now();
std::chrono::system_clock::time_point start_date = end_date - years(2);
years how_many = std::chrono::duration_cast<years>(end_date - start_date);
int how_many_as_int = how_many.count();
std::cout << how_many_as_int << std::endl;
std::unordered_map<int, std::list<whatever>> m;
m[how_many_as_int].push_back(...);

utc seconds since midnight to datetime

I'm getting radar data as "tracks" and the track data indicates the number of UTC seconds since the last midnight, apparently. This is not the number of seconds since the 1st of jan 1970.
Now I want to convert that to date time, knowing that the clock on the computer could be slightly out of sync with the clock on the radar. I'll assume the radar's seconds are the reference, not the computer's.
I want to convert these seconds to a full date time. Things seem to be a little tricky around
midnight.
Any suggestions? I've got some ideas, but I don't want to miss anything.
I'm working with C++ Qt.
// Function to extend truncated time, given the wall time and period, all
// in units of seconds.
//
// Example: Suppose the truncated period was one hour, and you were
// given a truncated time of 25 minutes after the hour. Then:
//
// o Actual time of 07:40:00 results in 07:25:00 (07:40 + -15)
// o Actual time of 07:10:00 results in 07:25:00 (07:10 + +15)
// o Actual time of 07:56:00 results in 08:25:00 (07:56 + +29)
double extendTruncatedTime(double trunc, double wall, int period) {
return wall + remainder(trunc - wall, period);
}
#define extendTruncatedTime24(t) extendTruncatedTime(t, time(0), 24 * 60 * 60)
Some commentary:
The units of wall are seconds, but its base can be arbitrary. In Unix, it typically starts at 1970.
Leap seconds are not relevant here.
You need #include <math.h> for remainder().
The period in extendTruncatedTime() is almost always twenty-four hours, 24 * 60 * 60, as per the OP's request. That is, given the time of day, it extends it by adding the year, month, and day of month, based on the 'wall' time.
The only exception I know to the previous statement is, since you mention radar, is in the Asterix CAT 1 data item I001/141, where the period is 512 seconds, and for which extendTruncatedTime() as given doesn't quite work.
And there is another important case which extendTruncatedTime() doesn't cover. Suppose you are given a truncated time consisting of the day of month, hour, and minute. How can you fill in the year and the month?
The following code snippet adds the year and month to a time derived from a DDHHMM format:
time_t extendTruncatedTimeDDHHMM(time_t trunc, time_t wall) {
struct tm retval = *gmtime_r(&trunc, &retval);
struct tm now = *gmtime_r(&wall, &now);
retval.tm_year = now.tm_year;
retval.tm_mon = now.tm_mon;
retval.tm_mon += now.tm_mday - retval.tm_mday > 15; // 15 = half-month
retval.tm_mon -= now.tm_mday - retval.tm_mday < -15;
return timegm(&retval);
}
As written, this doesn't handle erroneous inputs. For example, if today is July 4th, then the non-nonsensical 310000 will be quietly converted to July 1st. (This may be a feature, not a bug.)
If you can link against another lib, i'd suggest to use boost::date_time.
It seems you want to take current date in seconds from midnight (epoch) then add the radar time to it, then convert the sum back to a date time, and transform it into a string.
Using boost will help you in:
getting the right local time
calculating the date back
incorporating the drift into the calculation
taking leap seconds into account
since you'll have concept like time intervals and durations at your disposal. You can use something like (from the boost examples):
ptime t4(date(2002,May,31), hours(20)); //4 hours b/f midnight NY time
ptime t5 = us_eastern::local_to_utc(t4);
std::cout << to_simple_string(t4) << " in New York is "
<< to_simple_string(t5) << " UTC time "
<< std::endl;
If you want to calculate the drift by hand you can do time math easily similar to constructs like this:
ptime t2 = t1 - hours(5)- minutes(4)- seconds(2)- millisec(1);
I had the exact same problem but I'm using C#. My implementation is included here if anyone needs the solution in C#. This does not incorporate any clock drift.
DateTime UTCTime = DateTime.UtcNow.Date.AddSeconds(secondSinceMidnightFromRadar);

How do I detect if two dates straddle a weekend?

Problem
Given two datetimes, dt0 and dt1 (could be out of order), what is an algorithm which can determine if there is at least 24 hours worth of weekend (SAT, SUN) between the two dates?
Assume that there is a dayofweek() function that returns 0 for SUN, 1 for MON, etc...
Note: the problem is easy to visualize geometrically in terms of line segments, but the calculation eludes me for the moment.
Solution
The solution below will work for UTC, but it will fail for DST.
weekdayno() implementation not included: SUN==0, MON==1, etc...
isWeekday() is also not shown, but is trivial to implement once you have dayofweek()
binary operator-() implementation also not shown, but we simply convert both instances to UNIX-time (no. of secs since Epoch) and take the difference to yield the number of seconds between two DateTimes
hh() mm() ss() are just const accessors for returning hours, minutes, and seconds, respectively
James McNellis is right on the mark concerning DST.
Getting this code to work for the general DST case is non-trivial: need to add tz and anywhere you do any kind of date arithmetic requires careful consideration. Additional unit tests will be needed.
Lessons Learned
Query stackoverflow for various ways to look at a problem.
You can never have too many unit tests: need them to flush out weird edge cases
Use visualization, if possible, to look at a problem
What appears to be a trivial problem can actually be a bit tricky when you look at the details (eg DST).
Keep the solution as simple as possible because your code will very likely change: in order to fix for bugs/new test cases or in order to add new features (eg make it work for DST). Keep it as readable and easy to understand as possible: prefer algorithms over switch/cases.
Be brave and try things out: keep hammering at the solution until something works comes about. Use unit-tests so you can continuously refactor. It takes a lot of work to write simple code, but in the end, it's worth it.
Conclusion
The current solution is sufficient for my purposes (I will use UTC to avoid DST problems). I will select holygeek's Answer for his suggestion that I draw some ASCII art. In this case, doing so has helped me come up with an algorithm that is easy-to-understand and really, as simple as I can possibly make it. Thanks to all for contributing to the analysis of this problem.
static const size_t ONEDAYINSECS = (24 * 60 * 60);
DateTime
DateTime::nextSatMorning() const
{
// 0 is SUN, 6 is SAT
return *this + (6 - weekdayno()) * ONEDAYINSECS -
(((hh() * 60) + mm())*60 + ss());
}
DateTime
DateTime::previousSunNight() const
{
return *this - ((weekdayno() - 1 + 7)%7) * ONEDAYINSECS -
(((hh() * 60) + mm())*60 + ss());
}
bool
DateTime::straddles_24HofWeekend_OrMore( const DateTime& newDt ) const
{
const DateTime& t0 = min( *this, newDt );
const DateTime& t1 = max( *this, newDt );
// ------------------------------------
//
// <--+--F--+--S--+--S--+--M--+-->
// t0 ^ ^ t1
// +---->+ +<----|
// | |
// +<--nSecs-->+
// edge0 edge1
//
// ------------------------------------
DateTime edge0 = t0.isWeekday() ? t0.nextSatMorning() : t0;
DateTime edge1 = t1.isWeekday() ? t1.previousSunNight() : t1;
return (edge1 - edge0) > ONEDAYINSECS;
}
John Leidegren asked for my unit tests so here there are (using googletest)
Note that they pass for the non-DST cases above (running for UTC) - I expect the current implementation to fail for DST cases (haven't added them to the test cases below yet).
TEST( Test_DateTime, tryNextSatMorning )
{
DateTime mon{ 20010108, 81315 };
DateTime exp_sat{ 20010113, 0ul };
EXPECT_EQ( exp_sat, mon.nextSatMorning() );
}
TEST( Test_DateTime, tryPrevSunNight )
{
DateTime tue{ 20010109, 81315 };
DateTime exp_sun1{ 20010108, 0ul };
EXPECT_EQ( exp_sun1, tue.previousSunNight() );
DateTime sun{ 20010107, 81315 };
DateTime exp_sun2{ 20010101, 0ul };
EXPECT_EQ( exp_sun2, sun.previousSunNight() );
}
TEST( Test_DateTime, straddlesWeekend )
{
DateTime fri{ 20010105, 163125 };
DateTime sat{ 20010106, 101515 };
DateTime sun{ 20010107, 201521 };
DateTime mon{ 20010108, 81315 };
DateTime tue{ 20010109, 81315 };
EXPECT_FALSE( fri.straddles_24HofWeekend_OrMore( sat ));
EXPECT_TRUE( fri.straddles_24HofWeekend_OrMore( sun ));
EXPECT_TRUE( fri.straddles_24HofWeekend_OrMore( mon ));
EXPECT_TRUE( sat.straddles_24HofWeekend_OrMore( sun ));
EXPECT_TRUE( sat.straddles_24HofWeekend_OrMore( mon ));
EXPECT_FALSE( sun.straddles_24HofWeekend_OrMore( mon ));
EXPECT_TRUE( fri.straddles_24HofWeekend_OrMore( tue ));
EXPECT_FALSE( sun.straddles_24HofWeekend_OrMore( tue ));
}
This diagram may help:
SAT SUN
|---------|---------|
a---------b
a---------b
...
a---------b
The silliest way I can think of solving this is: copy the smaller datetime value, continuously add to it until it's either larger than the other datetime value (the bigger one) or dayofweek() doesn't equal 0 or 7 any more. Then check if the total value of time you added is less than 24 hours.
A slightly less silly way would be to check its a weekend, add 24 hours of time and then check once to make sure its a weekend and still less than the second datetime.
Daylight savings shouldn't really come into play as long as your function to find what day it is works.
Approach it in a structured manner: What are some of the simple/edge cases? What is the average case?
Simple cases I can think of off the top of my head (note, since you've already forced t0 to be the lower value, I'm assuming that below):
If t1 - t0 is less than 1 day, return false
If t1 - t0 is >= 6 days, return true (there's ALWAYS 24 hours of weekend time in any given 6 day block, even if you start on a Sunday).
Then we take dayofweek() for both t0 and t1, and do some checks (this is the average case). We can be extra cheap here now because we know t0 is only up to 5 days earlier than t1.
Edit: Removed my conditions because there were nasty little edge cases I wasn't considering. Anyway, the solution I recommended is still viable, I just won't do it here.
This site calculates business days in C#, does that help? http://www.infopathdev.com/forums/t/7156.aspx