Як разабраць 20180810T143000Z для time_t

Што гэта самы кароткі/самы элегантны спосаб (г.зн. выкарыстоўваць існуючыя Lib функцыі), каб разабраць радок у форме 20180810T143000Z у time_t ? Звярніце ўвагу, што ў літаральным сэнсе заўсёды ўяўляе сабой UTC змяняць час.

Я пачаў разбор радкі і прысваення значэнняў у STRUCT тм * тм для таго, каб зрабіць (TM паказваеце ёй) у канцы. Гэта адчувае сябе занадта складаным, хоць.

0
Які час бібліятэкі вы карыстаецеся? Я хацеў бы скапіяваць кожны неабходны знак з індэксам масіва ў буфер і пераўтварыць яго ў год, месяц, дзень, гадзіны, хвіліны. Пасля гэтага я хацеў бы выкарыстаць бібліятэку, каб пераўтварыць яго ў time_t для колькасці секунд, якія прайшлі з 1970 года Непасрэдная не надта складаным, на мой погляд. Ці можаце вы паказаць, што вы да гэтага часу? sscanf можа працаваць, але некаторыя лічаць, што гэта не элегантная функцыя.
дададзена аўтар Standback, крыніца

6 адказы

Разбор радкі на самай справе гэта адзіны шлях. Аднак ёсць шмат спосабаў зрабіць гэта.

Мой упадабаны метад для першай праверкі, што фармат з'яўляецца правільным, гледзячы на ​​Т-і Z быць у патрэбным месцы:

if (timeString[8] == 'T' && timeString[15] == 'Z') {
    ... parse in here
}

І сінтаксічны проста робяць колькасці з множаннем:

int year = (timeString[0] - '0') * 1000 +
           (timeString[1] - '0') * 100 +
           (timeString[2] - '0') * 10 +
           (timeString[3] - '0');

Вы можаце ачысціць рэчы з дапамогай макраса, калі вы хочаце:

#define NUM(off, mult) ((timeString[(off)] - '0') * (mult))

тады:

int year =   NUM(0, 1000) + NUM(1, 100) + NUM(2, 10) + NUM(3, 1);
int month =  NUM(4, 10)   + NUM(5, 1);
int day =    NUM(6, 10)   + NUM(7, 1);
int hour =   NUM(9, 10)   + NUM(10, 1);
int minute = NUM(11, 10)  + NUM(12, 1);
int second = NUM(13, 10)  + NUM(14, 1);

А потым, ды, змесціце іх у структура тм (ці непасрэдна прызначаць іх вынікі разлікаў без выкарыстання прамежкавых зменных) і называем паказваеце ёй() .

2
дададзена
@ MarcelStör Вядома - калі гэта тое, што вы хочаце зрабіць.
дададзена аўтар Majenko, крыніца
Я не магу сказаць вам, што. Гэта залежыць ад астатняй частцы вашай праграмы. Адказ на гэтае пытанне не можа быць "нічога", ці гэта можа быць «паведаміць карыстальніку», гэта «запыт на новую часовую пазнаку», або любую колькасць іншых рэчаў.
дададзена аўтар Majenko, крыніца
@Juraj Так, але вы не ўключылі ніводнага адказу на ваш адказ, толькі блок кода;)
дададзена аўтар Majenko, крыніца
@Majenko шкада, што я быў трохі неспецыфічныя. Я ўсё яшчэ знаёмыя з Arduino (як вы можаце сказаць). Тое, што я меў на ўвазе, што ў большасці іншых асяроддзяў/платформаў Я знаёмы з я, верагодна, кінуць выключэнне. Такім чынам, я лічу, вяртанне -1 і справіцца з гэтым за межамі функцыі зробіць гэта больш надзейным.
дададзена аўтар Krunal Hingu, крыніца
«Першы чэк, што фармат з'яўляецца правільным» - сапраўдны пункт, але што б вы зрабілі ў яшчэ галіны?
дададзена аўтар Krunal Hingu, крыніца

Вось яшчэ адзін спосаб, каб пераўтварыць радок пазнакі часу ў time_t. Ёсць некалькі выдатных адказаў ўжо тут, але вы можаце параўнаць двайковыя памеры файлаў. Гэты эскіз з'яўляецца 3884 байт (IDE версія 1.0.6.2, GCC 4.2.1).

#include 
TimeElements myTimeElements;
char timeString[] = "20180810T143000Z";

void setup(){

  Serial.begin(9600);

  myTimeElements.Year = CalendarYrToTm((timeString[0] - '0') * 1000 + (timeString[1] - '0') * 100 + (timeString[2] - '0') * 10 + (timeString[3] - '0'));
  myTimeElements.Month = (timeString[4] - '0') * 10 + (timeString[5] - '0');
  myTimeElements.Day = (timeString[6] - '0') * 10 + (timeString[7] - '0');
  myTimeElements.Hour = (timeString[9] - '0') * 10 + (timeString[10] - '0');
  myTimeElements.Minute = (timeString[11] - '0') * 10 + (timeString[12] - '0');
  myTimeElements.Second = (timeString[13] - '0') * 10 + (timeString[14] - '0');

 //Assemble time elements into time_t.
  time_t t = makeTime(myTimeElements);

 //Print out the contents of "t" one "piece" at a time using the "time_t" functions.
  Serial.println(year(t));
  Serial.println(month(t));
  Serial.println(day(t));
  Serial.println(hour(t));
  Serial.println(minute(t));
  Serial.println(second(t));

}

void loop(){}
1
дададзена

Гэта не будзе поўным без sscanf.

Я ўзяў эскіз па @Juraj і абвясціў паасобныя цэлыя лікі, каб быць упэўненымі, што кожны% d будзе адпавядаць з цэлым лікам.

#include 

void setup() {

  Serial.begin(115200);

  char buff[] = "20180810T143000Z";

  TimeElements tm;

  int yr, mnth, d, h, m, s;
  sscanf( buff, "%4d%2d%2dT%2d%2d%2dZ", &yr, &mnth, &d, &h, &m, &s);

  tm.Year = yr - 1970;
  tm.Month = mnth;
  tm.Day = d;
  tm.Hour = h;
  tm.Minute = m;
  tm.Second = s;

  time_t t = makeTime(tm);

  sprintf(buff, "%02d-%02d-%02d %02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t));

  Serial.println(buff);
}

void loop() {
}

Марсэль Штлр, зараз ёсць чатыры добрых рашэнняў. На мой погляд, яны аднолькава добрыя.

1
дададзена
#include 

void setup() {

  Serial.begin(115200);

  char buff[] = "20180810T143000Z";
  for (int i = 0; i < sizeof(buff); i++) {
    buff[i] = buff[i] - '0';
  }
  int yr = buff[0] * 1000 + buff[1] * 100 + buff[2] * 10 + buff[3];
  if (yr > 99)
    yr = yr - 1970;
  else
    yr += 30;
  TimeElements tm;
  tm.Year = yr;
  tm.Month = buff[4] * 10 + buff[5];
  tm.Day = buff[6] * 10 + buff[7];
 //8 T
  tm.Hour = buff[9] * 10 + buff[10];
  tm.Minute = buff[11] * 10 + buff[12];
  tm.Second = buff[13] * 10 + buff[14];

  time_t t = makeTime(tm);

  sprintf(buff, "%02d%02d%02d %02d%02d%02d", year(t), month(t), day(t), hour(t), minute(t), second(t));

  Serial.println(buff);

}

void loop() {

}

Вы можаце ўсталяваць TimeLib ў мэнэджару бібліятэк. Яна працуе на ўсіх платформах Arduino.

1
дададзена
У бафф [я ++] * 10 + станоўчы эфект [я ++] , парадак ацэнкі я ++ не вызначаны. Ён можа працаваць, як чакаецца, з якой-небудзь канкрэтнай камбінацыяй версіі кампілятара і налад, і няма на наступным. НКУ 5,4 папярэджвае мяне, што «аперацыя на" я "можа быць вызначанае». Правільны спосаб зрабіць гэта tm.Month = БАФ [я ++] * 10; tm.Month + = станоўчы эфект [я ++]; .
дададзена аўтар Sprogz, крыніца
@ MarcelStör: Тое, што я напісаў для tm.Month сапраўдны для кожнага асобніка, дзе я ++ з'яўляецца больш чым адзін раз у адным выразе.
дададзена аўтар Sprogz, крыніца
Re «кампілятар не мае ніякіх падставаў для ацэнкі [...]»: ён таксама не мае ніякіх падставаў, каб ацаніць іх у парадку, вы чакаеце! Калі ласка, даведацца пра кропкі паслядоўнасці ў C ++, перш чым прымаць такія няправільныя здагадкі. Звярніце ўвагу, у прыватнасці, што «Паміж папярэдняй і наступнай кропкай паслядоўнасці аб'ект павінен быць яго захаванае значэнне мадыфікаванага не больш за адзін раз у ходзе ацэнкі выразы.»
дададзена аўтар Sprogz, крыніца
змяніўся я ++ пастаянныя. (Год ранг # 4. Yabba Dabba ду)
дададзена аўтар Juraj, крыніца
у гэтым выпадку выкарыстанне I ++ добра. кампілятар не мае ніякіх падставаў, каб ацаніць некаторыя подвыражения з ++ я пазней у выразе перад некаторым подвыражением з ++ я раней у выразе. Я змяню яго. Я ўбачыў папярэджанне, але ў мяне было мала часу для гэтага.
дададзена аўтар Juraj, крыніца
@ MarcelStör, я прачытаў каментар Эдгара, але гэты код не нейкі фрагмент, але поўны працоўны прыклад. Я не люблю рэдагаваць яго без тэставання. І я не магу праверыць гэта цяпер.
дададзена аўтар Juraj, крыніца
@EdgarBonet Я ведаю, мае поўны сэнс. Менавіта таму прапанаваў, каб змяніць. У рэшце рэшт, гэта было нармальна, каб атрымаць адхілена, таму што шмат больш ліній на самай справе закрануты.
дададзена аўтар Krunal Hingu, крыніца
@EdgarBonet дзякуй, я проста прапанаваў гэта змена: arduino.stackexchange.com/review/suggested-edits/38715
дададзена аўтар Krunal Hingu, крыніца
Так, я гляджу ў strptime , але прыйшоў да высновы, што, верагодна, не працаваць без падзельнікаў. Таму я нават не спрабаваў.
дададзена аўтар Krunal Hingu, крыніца

Альтэрнатыва майго першага адказу будзе выкарыстоўваць стандартныя функцыі C з time.h і sscanf. C функцыя strptime не можа разабраць змяняць час без падзельнікаў. Але sscanf можа разабраць ўвод.

#include 

void setup() {

  Serial.begin(115200);

  const char* buff = "20180810T143000Z";

  tm tms;
  sscanf(buff, "%04d%02d%02dT%02d%02d%02d", &(tms.tm_year), &(tms.tm_mon), &(tms.tm_mday), &(tms.tm_hour), &(tms.tm_min), &(tms.tm_sec));
  tms.tm_year -= 1900;
  tms.tm_mon -= 1;
  tms.tm_isdst = 0;
  time_t t = mktime(&tms);

  Serial.println(ctime(&t));
}

void loop() {
}

У AVR «% 02hhd» павінен быць выкарыстаны, так як адпаведныя члены ў STRUCT ТМ int8_t.

1
дададзена
% D гэта цэлы лік, а таксама для AVR мікракантролера. Розніца заключаецца ў тым, што TimeLib TimeElements не выкарыстоўвае цэлыя лікі, але байты.
дададзена аўтар Standback, крыніца
Так, для C time.h tm_year і іншыя элементы ўсе цэлыя лікі. Акрамя таго, для мікракантролера AVR% D адпавядае цэламу ліку. % D чакае «Int», а не пераменная 2 ці 4 байта.
дададзена аўтар Standback, крыніца
Я паспрабаваў яго ў шэрагу розных спосабаў, з рознымі дадзенымі ў стэку, выкарыстоўваючы Arduino 1.8.5 з Arduino уно борце. A «INT» два байта, а «кароткі INT» таксама два байта і «кароткі кароткі ИНТ» не існуе. Фармат "% d» працуе з «міжнар» і «% HD», здаецца, зрабіць тое ж самае, як "% D». Фармат "% HHD» чытае падпісаныя байты. Усё, як я памятаю, ні дзіўных рэчаў. Калі вы можаце даказаць, што вы маеце рацыю з эскізам, вы можаце стварыць новую тэму для гэтага?
дададзена аўтар Standback, крыніца
Цяпер я гэта бачу, тм для АВР мае int8_t і int16_t элементаў. На жаль, яны на самой справе не ўсё цэлыя лікі для АВР. Я не ведаў, што. Дзякуючы. Ці згодныя вы, што% D у sscanf запалак міжнар 'для АВР? Я бачу, што вы выдалілі гэта заблытаная апошняя прапанова.
дададзена аўтар Standback, крыніца
% D на AVR працуе Sprintf, але sscanf няправільна чытаўся без чч. паспрабаваць
дададзена аўтар Juraj, крыніца
@Jot, гэта З time.h
дададзена аўтар Juraj, крыніца
@Jot, гэта толькі тады, калі я выкарыстоўваю элементы тм структуры як sscanf параметраў. Папярэджанне «% d» чакае аргумент тыпу «INT *», але аргумент 4 мае тып «int8_t * {ака падпісаны сімвал *}». і адсканаваныя значэння няслушныя. Пры рэгулярным Int зменнай чч не патрабуецца.
дададзена аўтар Juraj, крыніца
AVR мае int8_t tm_hour
дададзена аўтар Juraj, крыніца
Я выкарыстаў чч таму што папярэджанне кампілятара пра тое, што гэта int8_t і гэта дапамагло атрымаць правільныя дадзеныя. Я адкрыў time.h, але гэта было для ДУМДА або ЭСП і быў унутр. У мяне было мала часу, таму я не расследаваць яго далей. Вядома% d для міжнар. Я праверыў Int і выдаліць заўвагу. Потым я выявіў, што ў тм сапраўды unt8_t ў тм.
дададзена аўтар Juraj, крыніца

Дзеля паўнаты вось мой "адказ" працуе з Радок S, а не паўкокс [] .

time_t convertToTime(String calTimestamp) {
  struct tm tm;
  Serial.println("Parsing " + calTimestamp);
  String year = calTimestamp.substring(0, 4);
  String month = calTimestamp.substring(4, 6);
  if (month.startsWith("0")) {
    month = month.substring(1);
  }
  String day = calTimestamp.substring(6, 8);
  if (day.startsWith("0")) {
    month = day.substring(1);
  }
  tm.tm_year = year.toInt() - 1900;
  tm.tm_mon = month.toInt() - 1;
  tm.tm_mday = day.toInt();
  tm.tm_hour = calTimestamp.substring(9, 11).toInt();
  tm.tm_min = calTimestamp.substring(11, 13).toInt();
  tm.tm_sec = calTimestamp.substring(13, 15).toInt();
  return mktime(&tm);
}
1
дададзена
Цалкам верагодна. Метад Радок c_str() вяртае ўнутраны сімвал * паказальнік, так што ніякага рэальнага пераўтварэння не патрабуецца.
дададзена аўтар Sprogz, крыніца
Гэта павінна працаваць, але ўлічыце, што кожны выклік падрадок() метад вылучае новы радок ў кучы. Нават калі ў вас ёсць шмат вольнай памяці, шматлікія выклікі Таноса() і свабодны() можа зрабіць гэта вельмі неэфектыўна.
дададзена аўтар Sprogz, крыніца
Гэта бедныя бедныя кучы. Я адчуваю да яго ...
дададзена аўтар Majenko, крыніца
@EdgarBonet так, я ведаю, пункт прымаецца. Тое, што падаецца ў гэтай функцыі з'яўляецца (частковае) вынік адказу HTTP readStringUntil() . Я мяркую, што гэта было б больш эфектыўным спачатку пераўтварыць calTimestamp у сімвал [] ?
дададзена аўтар Krunal Hingu, крыніца