RTC trouble


#1

Hi guys, I don’t know how many of you have tried using the RTC in Pokitto, but I seem to be having a little trouble.
From what I have googled, to set the date+time, the following code should work -

time_t asUnixTime(int year, int mon, int mday, int hour, int min, int sec) {
    struct tm   t;
    t.tm_year = year - 1900;
    t.tm_mon =  mon;
    t.tm_mday = mday;
    t.tm_hour = hour;
    t.tm_min = min;
    t.tm_sec = sec;
    t.tm_isdst = -1;            // Is Daylight saving time on? 1 = yes, 0 = no, -1 = unknown

    return mktime(&t);          // returns seconds elapsed since January 1, 1970 (begin of the Epoch)
}

    set_time(asUnixTime(2000,1,1,0,0,0));  // 2000, jan, 1st, 00:00:00

So, I set the time to 00:00:00 Jan 1st 2000. So far so good, when I display the clock all seems OK, both in my code and in the loader, the time is set correctly. However, with the following,

time_t pokTime = time(NULL);
const char *wDays[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

/*
tm_sec	    int	    seconds after the minute	0-60*
tm_min	    int	    minutes after the hour  	0-59
tm_hour	    int	    hours since midnight    	0-23
tm_mday	    int	    day of the month        	1-31
tm_mon	    int	    months since January    	0-11
tm_year	    int	    years since 1900
tm_wday	    int	    days since Sunday	        0-6
tm_yday	    int	    days since January 1	    0-365
tm_isdst	int	    Daylight Saving Time flag
*/

    char tempText[255];

    struct tm tm = *localtime(&pokTime);

    sprintf(tempText, "%s %2d/%2d/%2d", wDays[tm.tm_wday],tm.tm_mday, tm.tm_mon, (tm.tm_year + 1900)%100);

I get the day of the week as Tuesday, however, 1/1/2000 was a Saturday. (https://www.timeanddate.com/calendar/?year=2000)

Can anyone see, have I messed a step or something?


#2

Did you remember to add 4 to the epoch?

// Calculate the current day of the week as an integer
//   now - Unix timestamp like that from time(NULL)
//   tz_offset - Number of hours off from UTC; i.e. PST = -8
//   Return value: Sunday=0, Monday=1, ... Saturday=6
int dayofweek(time_t now, int tz_offset) {
	// Calculate number of seconds since midnight 1 Jan 1970 local time
	time_t localtime = now + (tz_offset * 60 * 60);
	// Convert to number of days since 1 Jan 1970
	int days_since_epoch = localtime / 86400;
	// 1 Jan 1970 was a Thursday, so add 4 so Sunday is day 0, and mod 7
	int day_of_week = (days_since_epoch + 4) % 7; 

	return day_of_week;
}

Source:

https://gist.github.com/PhirePhly/fe30cdba68cc2241a4326a4c077963cb

#3

I also vaguely remember some discussion about mbed core lib returning wrong week day somewhere


#4

hmmm, that function returns tuesday also :frowning:


#5

What is the unformatted output in unix time (ticks)? Sorry I am at work, can’t look at my own Pokitto at the moment


#6

Don’t worry about it, I remembered having this same issue a while back with a DS project…

int dayOfWeek(int day,int month,int year){
  int a = (14 - month)/12;
  int y = year - a;
  int m = month + 12*a - 2;
  int d = (day + y + y/4 - y/100 + y/400 + (31*m)/12);
  return ((d % 7) + 1);
}

Dug this out of a project from 2007. It seems to work fine. It seems the internet is full of people incorrectly calculating the day of the week.


#7

Use <chrono> if it’s available.

#include <iostream>
#include <chrono>
#include <ctime>
 
int main()
{
    const auto p0 = std::chrono::time_point<std::chrono::system_clock>{};
    const auto p1 = std::chrono::system_clock::now();
    const auto p2 = p1 - std::chrono::hours(24);
 
    std::time_t epoch_time = std::chrono::system_clock::to_time_t(p0);
    std::cout << "epoch: " << std::ctime(&epoch_time);
	
    std::time_t today_time = std::chrono::system_clock::to_time_t(p1);
    std::cout << "today: " << std::ctime(&today_time);
 
    std::cout << "hours since epoch: " << std::chrono::duration_cast<std::chrono::hours>(
                   p1.time_since_epoch()).count()  << '\n';
    std::cout << "yesterday, hours since epoch: " << std::chrono::duration_cast<std::chrono::hours>(
                   p2.time_since_epoch()).count()  << '\n';
}

Possible output:

epoch: Thu Jan  1 00:00:00 1970
today: Fri Jun 30 10:44:11 2017
hours since epoch: 416338
yesterday, hours since epoch: 416314

Example stolen from https://en.cppreference.com/w/cpp/chrono/time_point/time_since_epoch

Obviously replace std::cout and <iostream> with Pokitto-specific printing techniques, but <chrono> should hopefully be available.

If you use C++14 you can even do const auto p2 = p1 - 24h;
(if you make sure to using namespace std::chrono_literals; first),
thanks to the added string literal operators.
(User string literals are available in C++11, it’s just that those operators weren’t added to the standard library until C++14.)


#8

I haven’t done a lot of testing, but tm_wday actually seems to be correct. It turns out that a minor inconstancy in the data format was tripping me up.

/*
tm_sec	    int	    seconds after the minute	0-60*
tm_min	    int	    minutes after the hour  	0-59
tm_hour	    int	    hours since midnight    	0-23

tm_mday	    int	    day of the month        	1-31 <----- THIS!!

tm_mon	    int	    months since January    	0-11
tm_year	    int	    years since 1900
tm_wday	    int	    days since Sunday	        0-6
tm_yday	    int	    days since January 1	    0-365
tm_isdst	int	    Daylight Saving Time flag
*/

For whatever reason, one and only one value starts at 1 and all of the others start at 0. Why?!?!?!?!


#9

Because C APIs/the C standard library.
(The same reason tm_isdst isn’t a bool.)


And technically it does actually match its definition.

It’s possible to have 0 seconds after the minute, 0 minutes after an hour et cetera,
but it’s not possible to have the 0th day of the month.
Cardinals vs ordinals.

If they’d defined it as “the number of days since the first day of the month” then it could have been 0-30.


#10

True, but as you say, it could easily be “the number of days since the first day of the month” just as all of the other fields are.


#11

Like I say, that’s the C standard library for you.
It has a lot of design flaws, of which using 1-31 instead of 0-30 is probably the least terrible.

It’s a bit of an artifact of the era from which it originates; an era where people were still learning what good API design is, and when they were more concerned with “it does the job now” than “what are the future ramifications of this decision?” (*cough* Y2K, Y2038).


That said, std::strftime is better than std::ctime at least.


#12

I agree on all your views there