How can I convert date and time to POSIX

Dear ALl,

I do have a SIM808 and I getting time with an AT command.

It return me the time in that form

"16/12/21,00:01:23+08"

I am working with another program called Ubidots, and it request POSIX format.

Is there a way to convert it with Arduino?

Thanl a lot for your help

Have not checked if those functions are part of the standard Libs but if they are You could try a combination of strptime() and mktime()

char timestamp[]="16/12/21,00:01:23+08";
struct tm tm;
time_t epochUnix;
if ( strptime(timestamp, "%Y/%m/%d,%T+08", &tm) != NULL ) {
  epochUnix = mktime(&tm); // I ignore the +08 piece may be you want to take this into account?
} else {
  // you have a bug
}

Did not try, so may be not exact - double check the format string labels for parsing. You can use %z if I remember correctly for the time zone offset although +08 feels not like a ISO 8601 offset from UTC...

There might also be a parseTime function in your library that you can use to just build the struct tm

Hello J-M-L

Thank for helping.

I do have a Feather M0's heart is an ATSAMD21G18 ARM Cortex M0 processor, clocked at 48 MHz and at 3.3V logic, the same one used in the new Arduino Zero.

May be is the cause of the following errors

mo808-mqtt:4831: error: 'mktime' was not declared in this scope

epochUTC = mktime(&tm);

C:\Users\pierrot\Documents\Arduino\mo808-mqtt\mo808-mqtt.ino: In function 'void get_Time(char*, char*, char*, char*, char*, char*, char*)':

mo808-mqtt:4828: error: aggregate 'get_Time(char*, char*, char*, char*, char*, char*, char*)::tm tm' has incomplete type and cannot be defined

struct tm tm;

mo808-mqtt:4830: error: 'strptime' was not declared in this scope

if ( strptime(timestamp, "%Y/%m/%d,%T+08", &tm) != NULL ) {

There is a special library for mktime and striptime?

Thank a lot

pierrot10:
Dear ALl,
Is there a way to convert it with Arduino?

So your question is about how to convert a timestamp from the UTC+08 time zone into a Unixtime (seconds since 1970/01/01 00:00 UTC using the C/C++ programming language?
Or what?

I would like to suggest:

parse the timestamp to get year, month day,hour,minute,second and time zone in numeric format from it, then for conversion

  • either use a function somebody else wrote
  • or write a function of your own

Hint: Until some date early in the year 2038, number of seconds will fit into a 'long' variable type.

Try with #include <time.h> - can't test right now, I'm on the go

Dear J-M-L

Thank for your help

I added #include <time.h> and the error desapeared.
However, it does not wolr well

Here is my code:

  /* Get POSIX format */
  char posix[]="16/12/22,00:01:23+04";
  struct tm tm;
  time_t epochUTC;
  if ( strptime(posix, "%Y/%m/%d,%T+04", &tm) != NULL ) {
    epochUTC = mktime(&tm); // I ignore the +04 piece may be you want to take this into account?
  } else {
  Serial.println(F("you have a bug with POSIX"));
  }
  Serial.print(F("POSIX time:")); Serial.println(epochUTC);

and her is the result

POSIX time:-1501996973

It's negatif and when I check the convertion in aweb page it return me

Sun, 28 May 1922 18:37:07 GMT

:slight_smile:

I saw a second problem

  if ( strptime(posix, "%Y/%m/%d,%T+04", &tm) != NULL ) {

I am not sure that, it's a good idea to have +04, as you wrote. In summer time, it can change to 08 and if I go to another country, it can change as well.

no?

Whatever, I do not care about +08 or +04. I only want to have UTC time, whereever, I am in the wolrd :slight_smile:

  time_t epochUTC;

  Serial.print(F("POSIX time:")); Serial.println(epochUTC);

You seem to think that println() is overloaded to print a time_t variable. You really should determine whether or not that is true. I already know.

I would check if %y would be even better for a two digit year.

pierrot10:
Whatever, I do not care about +08 or +04. I only want to have UTC time, whereever, I am in the wolrd :slight_smile:

I've tried to program something for you. No special libraries required.

Besides the epoch conversion code (it's in the setup() I also included two functions for isLeapYear() and daysInMonth() calculation.

bool isLeapYear(int yr)
{
  if (yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) return true;
  else return false;
}


byte daysInMonth(int yr,int m)
{
  byte days[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  if (m==2 && isLeapYear(yr)) return 29;
  else return days[m-1];
}

void setup()
{
  Serial.begin(9600);
  char timestamp[]="16/12/21,00:01:23+08";
  char format[]="%d/%d/%d,%d:%d:%d%d";
  int year,month,day,hour,minute,second,timezone;

  sscanf(timestamp,format,&year,&month,&day,&hour,&minute,&second,&timezone);
  if (year<100) year+=2000;

  Serial.println(year);
  Serial.println(month);
  Serial.println(day);
  Serial.println(hour);
  Serial.println(minute);
  Serial.println(second);
  Serial.println(timezone);

  long epoch=0;
  for (int yr=1970;yr<year;yr++)
  if (isLeapYear(yr)) epoch+=366*86400L;
  else epoch+=365*86400L;
  for(int m=1;m<month;m++) epoch+=daysInMonth(year,m)*86400L;
  epoch+=(day-1)*86400L;
  epoch+=hour*3600L;
  epoch +=minute*60;
  epoch +=second;
  epoch-=timezone*3600L;  
  Serial.print(F("POSIX time:")); 
  Serial.println(epoch);

What do you think?

That's great!! Thank a lot!!

epoch, can it be a int?
(my lib require a int)

pierrot10:
epoch, can it be a int?
(my lib require a int)

16-bit 'int' type in 8-bit microcontrollers like UNO is limited to 32767 in the positive range.

This is not even enough to hold the seconds of one single day, not to speak about all the seconds since January,1st, 1970.

So I used 'long', which is int32_t (a signed integer type 32-bit wide on AVR/Atmega platform like UNO).

'int' depends on platform. On 8-bit Arduino microcontrollers, int is 16-bit.

On 32-bit operating systems, 'int' is 32 bits wide.

On 64-bit operating system. 'int is sometimes 64 bits wide.

What exactly do you need? int16_t or int32_t or int64_t
An int may be many things, depending on compiler and platform where you are using 'int'.

On AVR Atmega 8-bit platform using AVR GCC as a compiler an 'int' is actually int16_t and a long is actually int32_t

Check out about 'int' with your compiler and platform and use at least int32_t for epoch!

Dear Jurs

I am sorry but I can no make working your code. It worked until I remove

char timestamp[]="16/12/21,00:01:23+08";

to get the time from the parameter.

I modified as the following

if ti is "16/12/22,23:09:16+04"
I got it converted as -1033158260

char buff[23];
long posix;

fona.getTime(buff, 23);
get_posix(buff);

Serial.print(F("posix:"));
Serial.println(posix);


void get_posix(const char * ti)
{
  
  Serial.print(F("to posix:")); 
  Serial.println(ti);
  // gsm_time format: "16/12/21,00:01:23+08";

  
  //char ti[] = "16/12/21,00:01:23+08";
  // char ti[23];
  //strncpy(ti, t, 23); 
  
 char format[23]="%d/%d/%d,%d:%d:%d%d"; // if I remove 23, it bugs
  int year,month,day,hour,minute,second,timezone;

  sscanf(ti,format,&year,&month,&day,&hour,&minute,&second,&timezone);
  if (year<100) year+=2000;

Serial.print(F("to p4"));
  /*
  Serial.println(year);
  Serial.println(month);
  Serial.println(day);
  Serial.println(hour);
  Serial.println(minute);
  Serial.println(second);
  Serial.println(timezone);
  */

  long epoch=0;
  for (int yr=1970;yr<year;yr++)
  if (isLeapYear(yr)) epoch+=366*86400L;
  else epoch+=365*86400L;
  for(int m=1;m<month;m++) epoch+=daysInMonth(year,m)*86400L;
  epoch+=(day-1)*86400L;
  epoch+=hour*3600L;
  epoch +=minute*60;
  epoch +=second;
  epoch-=timezone*3600L;  


  //Serial.println(epoch); 
  posix = epoch;
}



byte daysInMonth(int yr,int m)
{
  byte days[12]={31,28,31,30,31,30,31,31,30,31,30,31};
  if (m==2 && isLeapYear(yr)) return 29;
  else return days[m-1];
}
bool isLeapYear(int yr)
{
  if (yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) return true;
  else return false;
}

What would you suggest me?
I do nt understand, it seam not so complicated!

NB: Idealy, it's to have posix in double but without decimal

Make it a uint32_t not a long if you do math manually

Hello J-M-L

I thought about that, but ajson only accept double or int

and I want to have it sent to Ubidot without decimal and it does make sens to have a posix with 5 decimal

How large is an int in your architecture?

Hello

not large

int are only speed, heading and may another I misses.

I also have to send stamp but it's too long to be a int

timestamp Optional timestamp in milliseconds, according to the POSIX standard.
Example: "timestamp":1376056359000

So I could use double but without decimal.

Ok, I think, I understanf my problem.
Any way, there is a problem in my coce, I just realise it now.
I am stupid

this

fona.getTime(buff, 23)

return

"16/12/22,23:58:37+04"

and not

16/12/22,23:58:37+04

and of course, the " make an issue.

I am going to remove it and see the difference.

Indeed! Just start Parsing after the first "