hi
how to split this string 2015-01-01T01:00:00 into int year,month,days,hour,minute,date ?
any help is appreciated.
Thanks
hi
how to split this string 2015-01-01T01:00:00 into int year,month,days,hour,minute,date ?
any help is appreciated.
Thanks
if the string has fixed positions it is rather easy, pseudo code
s = "2015-01-01T01:00:00"
idx = 0;
year= 0;
month = 0;
while (isdigit(s[idx]) )
{
 year *= 10;
 year += s[idx] - '0';
 idx++;
}
idx++; // skip -
while (isdigit(s[idx]) )
{
 month *= 10;
 month += s[idx] - '0';
 idx++;
}
// other fields in a similar way
yes it can even be shorter
unsigned long year = s[0]*1000UL + s[1]*100UL+s[2]*10UL + s[3] - 53328UL;
unsigned int month = s[5]*10 + s[6] - 528;
unsigned int day = s[7]*10 + s[8] - 528;
unsigned int hour= s[10]*10 + s[11] - 528;
unsigned int min= s[13]*10 + s[14] - 528;
unsigned int sec = s[16]*10 + s[17] - 528;
the magic numbers are corrections for char to int.
and using modulo magic you can shorten this and lower the memory footprint too
(not tested for all possible strings disclaimer applies)
char s[] = "2015-01-01T01:00:00";
void setup()
{
 Serial.begin(115200);
 Serial.print("Start ");
 Serial.println(__FILE__);
 // unsigned long year = s[0] * 1000UL + s[1] * 100UL + s[2] * 10UL + s[3] - 53328UL;
 // unsigned int month = s[5] * 10 + s[6] - 528;
 // unsigned int day = s[8] * 10 + s[9] - 528;
 // unsigned int hour = s[11] * 10 + s[12] - 528;
 // unsigned int min = s[14] * 10 + s[15] - 528;
 // unsigned int sec = s[17] * 10 + s[18] - 528;
 int year = s[0] * 1000 + s[1] * 100 + s[2] * 10 + s[3] + 12208;
 byte month = s[5] * 10 + s[6] - 528;
 byte day = s[8] * 10 + s[9] - 528;
 byte hour = s[11] * 10 + s[12] - 528;
 byte min = s[14] * 10 + s[15] - 528;
 byte sec = s[17] * 10 + s[18] - 528;
 Serial.println(year);
 Serial.println(month);
 Serial.println(day);
 Serial.println(hour);
 Serial.println(min);
 Serial.println(sec);
}
void loop()
{
}
update:
a quick test shows 20uSec versus 12 uSec (modulo version)
modulo version uses 7 bytes less RAM
the magic number are coming from '0' * 10 + '0' interpreted as int (also *100 and *1000 for year)
Note this works for the above fixed string formats, but can be extended to any "stringed number"
Awesome!
It really work.
Thanks Rob Tillaart
@admnelson32exe, don't forget to include a short comment line to explain the magic.
Nice trick Rob. I wasn't aware of this method, and probably would have used atoi().
'aarg', I just did exactly that in my example code, so I won't forget.
aarg:
@admnelson32exe, don't forget to include a short comment line to explain the magic.
Think to explain it properly a short comment could be longer than you think
@OldSteve
How did you comment it?
Ãnt month = (s[6] - '0') * 10 + s[7] - '0'; // 3x ADD 1x MUL
<>
Ãnt month = (s[6] - 48) * 10 + s[7] - 48;
<>
Ãnt month = s[6] * 10 - 480 + s[7] - 48;
<>
Ãnt month = s[6] * 10 + s[7] - 528; // 2x ADD 1x MUL
for the 4 digit year one saves 3x ADD
the relation between magic numbers -53328UL; and +12208 is that they are the same when the addition is done modulo 65536.
In same way one can convert a time like "12:34:56" to seconds fast
robtillaart:
Think to explain it properly a short comment could be longer than you think
My comment is short and easy to understand.
I copied what you said, with a few words added:-
The magic numbers are corrections for char to int, and are derived as follows:-
'0' * 10 + '0' interpreted as int (also *100 and *1000 for year)
Rob, there was a small error in the following version, (very easily fixed):-
unsigned long year = s[0]*1000UL + s[1]*100UL+s[2]*10UL + s[3] - 53328UL;
unsigned int month = s[5]*10 + s[6] - 528;
unsigned int day = s[7]*10 + s[8] - 528;
unsigned int hour= s[10]*10 + s[11] - 528;
unsigned int min= s[13]*10 + s[14] - 528;
unsigned int sec = s[16]*10 + s[17] - 528;
From 'day' onwards, the array indices were out by one, and should have been:-
unsigned long year = s[0]*1000UL + s[1]*100UL+s[2]*10UL + s[3] - 53328UL;
unsigned int month = s[5]*10 + s[6] - 528;
unsigned int day = s[8]*10 + s[9] - 528;
unsigned int hour= s[11]*10 + s[12] - 528;
unsigned int min= s[14]*10 + s[15] - 528;
unsigned int sec = s[17]*10 + s[18] - 528;
robtillaart:
Think to explain it properly a short comment could be longer than you think
Don't go there. That could turn into a reason to not do it.
But I would say something like, "// Constant ASCII offsets are combined and then subtracted from the result".
Or say:
unsigned int hour= s[11]*10 + s[12] - int('0' * 10 + '0');
Well spotted OldSteve!
saw it too, posted the idea before testing.
did a performance comparison
//
//Â Â FILE: splitDateTimeFast.ino
//Â AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// PURPOSE: demo
//Â Â DATE: 2015-10-07
//Â Â URL:
//
// Released to the public domain
//
uint32_t start;
uint32_t stop;
volatile char s[] = "2015-01-01T12:34:56";
volatile unsigned long yy;
volatile unsigned int mm;
volatile unsigned int dd;
volatile unsigned int hh;
volatile unsigned int mi;
volatile unsigned int se;
volatile int year;
volatile byte month;
volatile byte day;
volatile byte hour;
volatile byte min;
volatile byte sec;
void setup()
{
 Serial.begin(115200);
 Serial.print("Start ");
 Serial.println(__FILE__);
 delay(1000);
 start = micros();
 for (int i = 0; i < 1000; i++)
 {
  yy = s[0] * 1000UL + s[1] * 100UL + s[2] * 10UL + s[3] - 53328UL;
  mm = s[5] * 10 + s[6] - 528;
  dd = s[8] * 10 + s[9] - 528;
  hh = s[11] * 10 + s[12] - 528;
  mi = s[14] * 10 + s[15] - 528;
  se = s[17] * 10 + s[18] - 528;
 }
 stop = micros();
 Serial.println(yy);
 Serial.println(mm);
 Serial.println(dd);
 Serial.println(hh);
 Serial.println(mi);
 Serial.println(se);
 Serial.println(stop - start);
 Serial.println();
 delay(1000);
 start = micros();
 for (int i = 0; i < 1000; i++)
 {
  year = s[0] * 1000 + s[1] * 100 + s[2] * 10 + s[3] - 53328;
  month = s[5] * 10 + s[6] - 528;
  day = s[8] * 10 + s[9] - 528;
  hour = s[11] * 10 + s[12] - 528;
  min = s[14] * 10 + s[15] - 528;
  sec = s[17] * 10 + s[18] - 528;
 }
 stop = micros();
 Serial.println(year);
 Serial.println(month);
 Serial.println(day);
 Serial.println(hour);
 Serial.println(min);
 Serial.println(sec);
 Serial.println(stop - start);
 Serial.println();
 // Conditional MUL?
 delay(1000);
 start = micros();
 for (int i = 0; i < 1000; i++)
 {
  year = s[3] - '0';
  int t = s[0] - '0';
  if (t > 0) year += t * 1000;
  t = s[1] - '0';
  if (t > 0) year += t * 100;
  t = s[2] - '0';
  if (t > 0) year += t * 10;
  month = s[6] - '0';
  if (s[5] != '0') month += 10;
  day = s[9] - 48;
  t = s[8] - '0';
  if (t > 0) day += t * 10;
  hour = s[12] - 48;
  t = s[11] - '0';
  if (t > 0) hour += t * 10;
  min = s[15] - 48;
  t = s[14] - '0';
  if (t > 0) min += t * 10;
  sec = s[18] - 48;
  t = s[17] - '0';
  if (t > 0) sec += t * 10;
 }
 stop = micros();
 Serial.println(year);
 Serial.println(month);
 Serial.println(day);
 Serial.println(hour);
 Serial.println(min);
 Serial.println(sec);
 Serial.println(stop - start);
 Serial.println();
}
void loop()
{
}
output:
Start splitDateTimeFast.ino
...
16412
...
6412
...
11448
Second implementation is substantially faster (factor 2.6, all loops same overhead ~300uSec)
Conditionally math is not worth the code.
oops observation:
year does not need to be a unsigned long in first code as the year 9999 becomes 63327 before correction.
that removes all UL math ==> redo the performance test...
now the first one runs in 8872,
so the second implementation is still fastest (due to smaller datatypes)
(enough cpu cycles squeezed for now)