Time_t uchun 20180810T143000Zni qanday ajratish kerak

20180810T143000Z shaklida mag'lubiyatni time_t ga ajratish uchun eng qisqa/eng oqlangan uslub (ya'ni, mavjud lib vazifalarini ishlatish) nima? Ahamiyat bering, har doim UTC vaqt tamg'asini bildiradi.

So'ngra mktime (tm) ni bajarish uchun stringni tahlil qila boshladim va qiymatlarni struct tm * tm ga tayinladim. Biroq, bu juda murakkab.

0
Qaysi kutubxonadan foydalanasiz? Har bir kerakli belgi qator indekslari bilan buferga nusxa ko'chirib, uni yil, oy, kun, soat, daqiqaga aylantiraman. Shundan so'ng kutubxonani 1970 yildan beri soniyasiga sanab turish uchun kutubxonadan foydalanardim. Menimcha, qarama-qarshilik juda murakkab emas. Siz hozirgacha nimalar qilganingizni ko'rsatasizmi? sscanf ishlaydi, lekin ba'zilar bu oqlangan funksiya emas deb o'ylashadi.
qo'shib qo'ydi muallif Standback, manba

6 javoblar

Jiringlashni chizish aslida bitta usul. Ammo buni amalga oshirishning ko'plab usullari mavjud.

Afzal usulim avval T va Z ni to'g'ri joyda bo'lishini bilish orqali formatning to'g'ri ekanligini tekshirish:

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

Va ajralish faqat sonni ko'paytirmoqchi:

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

Siz xohlagan narsalarni so'l bilan tozalashingiz mumkin:

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

Keyin:

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);

Va keyin, ha, ularni struct tm ga qo'ying (yoki qidiruv parametrlardan foydalanmasdan ularni hisoblash natijalarini to'g'ridan-to'g'ri tayinlang) va mktime() ni chaqiring.

2
qo'shib qo'ydi
@ MarcelStor Albatta - bu siz xohlagan narsa bo'lsa.
qo'shib qo'ydi muallif Majenko, manba
Buni sizga aytolmayman. Sizning dasturingiz qolganiga bog'liq. Javob "hech narsa" bo'lishi mumkin yoki "foydalanuvchiga gapirib berish", "yangi vaqt tamg'asi talab qilish" yoki boshqa biron-bir boshqa narsalar bo'lishi mumkin.
qo'shib qo'ydi muallif Majenko, manba
@Juraj Ha, lekin javobingizda biron bir javob kiritmadingiz, faqat kod bloki;)
qo'shib qo'ydi muallif Majenko, manba
@Majenko uchun uzr so'rayman, men biroz noma'lum edim. Men hali Arduino bilan tanishib chiqyapman. Men nimani nazarda tutgan bo'lsam, men biladigan boshqa ko'pchilik muhitlarda/platformalarda, ehtimol, istisno qilmoqchi edim. Shunday qilib, men qaytib kelgan -1ga ishonaman va uni funksiya tashqarisidan ishlating, bu yanada mustahkam qiladi.
qo'shib qo'ydi muallif Krunal Hingu, manba
"avval formatning to'g'ri ekanligini tekshiring" - haqiqiy nuqta, lekin else filialida nima qilasiz?
qo'shib qo'ydi muallif Krunal Hingu, manba

Vaqt tamg'asi mag'lubiyatini time_t ga o'zgartiradigan yana bir usul. Bu yerda allaqachon bir qancha ajoyib javoblar mavjud, lekin ikkilik fayl o'lchamlarini solishtirish mumkin. Ushbu eskizning qiymati 3884 baytdir (IDE Version 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
qo'shib qo'ydi

Bu sscanfsiz to'liq emas.

@Juraj tomonidan sketchni oldi va har bir% d butun son bilan mos kelishiga ishonch hosil qilish uchun alohida sonlarni e'lon qildim.

#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() {
}

Marcel Stor, hozir to'rtta yaxshi echim bor. Menimcha, ular teng darajada yaxshi.

1
qo'shib qo'ydi
#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 ni Library Manager'da o'rnatishingiz mumkin. Barcha Arduino platformalarida ishlaydi.

1
qo'shib qo'ydi
buff [i ++] * 10 + buff [i ++] da i ++ ning baholash tartibi aniqlanmagan. Ushbu kompilyator versiyasi va sozlamalarining istalgan kombinatsiyasi bilan kutilganidek ishlashi mumkin va keyingi ishlamay qolishi mumkin. gcc 5.4 "menga" operatsiyasi aniqlanmagan bo'lishi mumkinligi haqida ogohlantiradi. Buning to'g'ri usuli tm.Month = buff [i ++] * 10; tm.Month + = buff [i ++]; .
qo'shib qo'ydi muallif Sprogz, manba
@ MarcelStor: tm.Month uchun yozgan narsam bir xil ifodada bir martadan ortiq ko'rinadigan har bir misol uchun amal qiladi.
qo'shib qo'ydi muallif Sprogz, manba
"Derivatorni baholash uchun hech qanday asos yo'q [...]": ularni siz kutgan tartibda baholash uchun hech qanday sabab yo'q! Iltimos, bunday noto'g'ri taxminlar kiritmasdan oldin C ++ da ketma-ketliklarni bilib oling. Ayniqsa, "avvalgi va keyingi navbatdagi nuqta o'rtasida ob'ekt, ifoda qiymatini baholash yo'li bilan o'zgartirilgan qiymatiga ega bo'lishi kerak".
qo'shib qo'ydi muallif Sprogz, manba
i ++ dan sobit bo'lgan. (Yilning 4-darajali yabba dabba doo)
qo'shib qo'ydi muallif Juraj, manba
Bu holda i ++ dan foydalanish yaxshi. kompilyatorda i ++ yordamida keyingi ba'zi iboralarni i ++ bilan oldingi ifoda bilan ifodalashda ba'zi subkeysmeytsiyalarni baholash uchun hech qanday sabab yo'q. Men uni o'zgartiraman. Men ogohlantirishni ko'rdim, lekin bunga cheklangan vaqt bor edi.
qo'shib qo'ydi muallif Juraj, manba
@ MarcelStor, Edgarning fikrini o'qiyapman, lekin bu kod bir qism emas, balki to'liq misol. Men uni sinovdan o'tkazishni yoqtirmayman. Va hozir uni sinab ko'rmayapman.
qo'shib qo'ydi muallif Juraj, manba
@EdgarBonet Bilaman, butunlay mantiqan. Shuning uchun bu tahrirni taklif qildilar. Oxir-oqibat, rad etilganlarni qabul qilishning iloji yo'q edi, chunki ko'plab chiziqlar aslida ta'sirlangan.
qo'shib qo'ydi muallif Krunal Hingu, manba
@EdgarBonet rahmat, men faqat shu o'zgarishlarni taklif qildim: arduino.stackexchange.com/review/suggested-edits/38715
qo'shib qo'ydi muallif Krunal Hingu, manba
Ha, men strptime ga qaradim, lekin ehtimol bu cheklovchilar bilan ishlamaydi degan xulosaga keldi. Shuning uchun men hatto harakat qilmadim.
qo'shib qo'ydi muallif Krunal Hingu, manba

Birinchi javobimga alternativa - time.h va sscanf dan standart S funktsiyalaridan foydalanish. C funktsiyasi strptime vaqt belgilari chegarachilarsiz ajratib bo'lolmaydi. Lekin sscanf sizning ma'lumotlaringizni tekshirishi mumkin.

#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() {
}

AVRda "% 02hhd" ishlatilishi kerak, chunki strukturadagi barcha a'zolar int8_t hisoblanadi.

1
qo'shib qo'ydi
% D, bir avr mikrokontrolör uchun ham tamsayıdır. Farq shundaki, TimeLib TimeElements tamsayılardan emas, balki baytlardan foydalanmaydi.
qo'shib qo'ydi muallif Standback, manba
Ha, C time.h uchun tm_year va boshqa elementlar butun sonlardir. Bundan tashqari, avr mikrokontrollayıcısı uchun% d bir tamsayıya mos keladi. % D% 2 yoki 4 bayt o'zgaruvchini emas, balki int ni kutadi.
qo'shib qo'ydi muallif Standback, manba
Arduino 1.8.5 versiyasini ishlatib, uni ketma-ketlikdagi turli ma'lumotlar bilan bir necha usulda sinab ko'rdim. "Int" - ikki bayt, "qisqa int" ham ikki bayta va "qisqa qisqa int" mavjud emas. "% D" formati "int" bilan ishlaydi, "% hd" esa "% d" bilan o'xshash ko'rinadi. "% Hhd" formati imzolangan baytni o'qiydi. Hamma narsa men eslayman, g'alati narsalar yo'q. Agar eskiz bilan to'g'ri ekanligini isbotlasangiz, unda yangi mavzu boshlaysizmi?
qo'shib qo'ydi muallif Standback, manba
Endi buni ko'rib turibman, avr uchun tm int8_t va int16_t elementlariga ega. Kechirasiz, ular aslida avr uchun butun son emas. Buni bilmasdim. Rahmat. Sscanf ichida% d "int" uchun avr uchun mos keladimi? Men sizning oxirgi gapingizni chalkashtirib yuborganingizni ko'raman.
qo'shib qo'ydi muallif Standback, manba
AVR bo'yicha% d sprintf uchun ishlaydi, ammo sscanf hh holda noto'g'ri o'qing. Urunib ko'r
qo'shib qo'ydi muallif Juraj, manba
@ Jot, bu C time.h
qo'shib qo'ydi muallif Juraj, manba
@ Jot, faqat tm struct a'zolaridan sscanf parametrlari sifatida foydalansam bo'ladi. Ushbu ogohlantirish "% d" "int *" turidagi dalillarni kutadi, ammo argument 4 "int8_t * {aka signed char *}" shakliga ega. skanerlashtirilgan qiymatlar noto'g'ri. Muntazam int o'zgaruvchisi bilan hh kerak emas.
qo'shib qo'ydi muallif Juraj, manba
AVRda int8_t tm_hour mavjud
qo'shib qo'ydi muallif Juraj, manba
Men hh foydalanardim, chunki derleyici ogohlantirishi int8_t ekanligini va u to'g'ri ma'lumotlarni olishga yordam berdi. Vaqtni ochdim, lekin samd yoki esp uchun edi va int bor edi. Menda cheklangan vaqt bor edi, shuning uchun men uni tadqiq qilmaganman. Albatta,% d int uchun. Int ni sinab ko'rdim va eslatmani olib tashladim. Keyin men tmda tm ning unt8_t ekanligini aniqladim.
qo'shib qo'ydi muallif Juraj, manba

To'liqlik uchun bu erda char [] o'rniga String s bilan ishlaydigan mening "javob".

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
qo'shib qo'ydi
Ko'p ehtimol. String usuli c_str() ichki char * ko'rsatgichini qaytaradi, shuning uchun haqiqiy o'tkazish kerak emas.
qo'shib qo'ydi muallif Sprogz, manba
Bu ishlashi kerak, lekin har qanday qo'ng'iroqni substring() usuliga yangi satr ajratadi. Ko'p bo'sh xotira bo'lsa ham, malloc() va free() uchun bir nechta qo'ng'iroq bu juda samarasiz bo'lishi mumkin.
qo'shib qo'ydi muallif Sprogz, manba
Bu yomon kambag'al uyum. Buni his qilaman ...
qo'shib qo'ydi muallif Majenko, manba
@EdgarBonet Ha, bilaman, olingan nuqta. Ushbu funktsiyaga qanday oziqlangani HTTP javobining readStringUntil() (qisman) natijasidir. Birinchidan calTimestamp kodini char [] ga aylantirish uchun yanada samarali bo'lardi, deb o'ylayman?
qo'shib qo'ydi muallif Krunal Hingu, manba