Rev 2136 |
Go to most recent revision |
Blame |
Last modification |
View Log
| RSS feed
/*****************************************************************************
* Copyright (C) 2013 Oliver Gemesi *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
*****************************************************************************/
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+ xutils.c - erweiterte String-Funktionen, xprintf (Info nach History)
//+ und weitere Hilfsfunktionen wie z.B. UTCdatetime2local()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//############################################################################
//# HISTORY xutils.c
//#
//# 12.04.2014 OG
//# - chg: strncpyat(), strncpyat_P(), _strncpyat() erweitert um Parameter 'sepcharcount'
//#
//# 08.04.2014 OG
//# - add: strncpyat(), strncpyat_P(), _strncpyat()
//#
//# 28.02.2014 OG
//# - add: buffered_sprintf(), buffered_sprintf_P()
//# - add: buffer sprintf_buffer[]
//# - chg: PGMBUFF_SIZE und ARGBUFF_SIZE von 80 auf 40 geaendert
//#
//# 24.06.2013 OG
//# - add: strrtrim() entfernt Leerzeichen auf der rechten Seite
//#
//# 14.05.2013 OG
//# - chg: Kommentarkopf von UTCdatetime2local() aktualisiert
//#
//# 05.05.2013 OG
//# - chg: UTCdatetime2local() auf Config.timezone/summertime umgestellt
//# - add: include eeprom.h
//#
//# 04.05.2013 OG
//# - chg: umbenannt zu xutils.c
//#
//# 03.05.2013 OG
//# - add: UTCdatetime2local()
//# - fix: _xvsnprintf() Darstellung kleiner negativer Nachkommazahlen (-1<z<0)
//#
//# 29.04.2013 OG
//# - chg: Doku zu xprintf Ergaenzt bei 'bekannte Einschraenkungen'
//# - chg: includes reduziert auf das Notwendige
//#
//# 28.04.2013 OG - NEU
//############################################################################
//############################################################################
//# xprintf
//#
//# Diese Variante von printf ist angepasst auf das PKT:
//#
//# - Unterstuetzung von Festkomma-Integer Anzeige
//# - Overflow-Anzeige durch '*' wenn eine Zahl die Format-Maske sprengt
//# - progmen wird optional unterstuetz fuer den format-String als auch fuer
//# String Argumente
//# - strikte Einhaltung von Laengen einer Format-Maske
//# (ggf. wird die Ausgabe gekuerzt)
//#
//# In diesem Source sind nur die Basis-xprintf zum Erzeugen von formatierten
//# Strings. Die direkten Ausgabefunktionen auf den Screen sind in lcd.c
//# als lcdx_printf_at() und lcdx_printf_at_P().
//#
//# FORMAT ANGABEN:
//#
//# %d: dezimal int signed (Rechtsbuendige Ausgabe) (Wandlung via itoa)
//# "%d" arg: 1234 -> "1234"
//# "%5d" arg: 1234 -> " 1234"
//# "%5.2d" arg: 1234 -> " 12.34"
//# "%05d" arg: 123 -> "00123"
//# "%3.2d" arg: -13 -> " -0.13"
//#
//# %u: dezimal int unsigned (Rechtsbuendige Ausgabe) (Wandlung via utoa)
//# wie %d jedoch mittels utoa
//#
//# %h: hex int unsigned -> Hex-Zahl Rechtsbuendig, Laenge wird unterstuetzt z.B. "%4h"
//# %o: octal int unsigned -> Octal-Zahl Rechtsbuendig, Laenge wird unterstuetzt z.B. "%2o"
//# %b: binary int unsigned -> Binaer-Zahl Rechtsbuendig, Laenge wird unterstuetzt z.B. "%8b"
//#
//# %ld, %lu, %lh, %lo, %lb:
//# wie die obigen jedoch fuer long-zahlen (via ltoa, ultoa)
//# nur wenn define USE_XPRINTF_LONG gesetzt ist!
//# Bespiele: "%ld", "%5.6ld" usw.
//#
//# %s: String aus RAM -> Linksbuendig, Laenge wird unterstuetzt (z.B. "%8s")
//# %S: String aus progmem -> Linksbuendig, Laenge wird unterstuetzt (z.B. "%8s")
//#
//# %c: einzelnes char -> Linksbuendig, Laenge wird unterstuetzt (z.B. "%8s")
//# %%: Ausgabe von "%"
//#
//#
//# BEISPIELE:
//#
//# vorhanden in: osd/osdata.c, lcd/lcd.c
//#
//#
//# BEKANNTE EINSCHRAENKUNGEN:
//#
//# 1. padding '0' mit negativen Zahlen wenn padding aktiv ist ergibt z.B. "00-1.5"
//# 2. der Entwickler muss auf eine korrekte Format-Maske achten. Andernfalls
//# kommt es zu nicht vorhersehbaren Ausgaben.
//# 3. nicht in ISR's verwenden - dazu muss ggf. eine Anpassung durchgefuert werden
//#
//# KOMPILIER OPTIONEN
//#
//# define: USE_XPRINTF_LONG
//# Unterstuetzung von long int / unsigned long int ('l' Modifier in Maske)
// Wird in der main.h gesetzt
//############################################################################
#define USE_XPRINTF_LONG // Unterstuetzung von long integer - Achtung! Muss fuer PKT gesetzt sein
// da in Sourcen verwendet!
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include "../main.h"
#include "../timer/timer.h"
#include "../eeprom/eeprom.h"
#define PGMBUFF_SIZE 40 // max. 40 chars bei format-String aus progmem
#define ARGBUFF_SIZE 40 // buffer fuer xprintf Parameter (strings, itoa, utoa) (fuer ltoa ggf. anpassen)
#define SPRINTF_BUFFER_SIZE 40 // max. 40 Chars fuer den Buffer fuer xsnprintf(), xsnprintf_P()
char sprintf_buffer
[SPRINTF_BUFFER_SIZE
];
//----------------------
// xprintf parser data
//----------------------
typedef struct
{
uint8_t parse
; // true / false
char cmd
; // char: d u h o b s S c %
uint8_t prelen
; // Vorkomma
uint8_t declen
; // decimal (Nachkomma)
uint8_t point
; // true / false
uint8_t uselong
; // true / false
uint8_t mask
; // true / false
char pad
; // ' ' oder '0'
} xprintf_t
;
//----------------------
// Buffers
//----------------------
char cmddigit
[7];
char argbuff
[ARGBUFF_SIZE
];
char pgmbuff
[PGMBUFF_SIZE
];
//####################################################################################
//---------------------------------------------------------------------
// Basisfunktion von xprintf
// Doku: siehe oben
//---------------------------------------------------------------------
void _xvsnprintf
( uint8_t useprogmem
, char *buffer
, uint8_t n
, const char *format
, va_list ap
)
{
const char *p_fmt
; // pointer auf den format-String
char *p_dst
; // pointer auf den destination buffer
const char *p_str
; // pointer auf einen arg-String wenn ein String ausgegeben werden soll
char *p_cmddigit
= 0; // pointer auf den Digit-Buffer fuer eine Maske (Laenge/Nachkommastellen)
const char *p_fmtbuff
; // pointer auf den format-Buffer (fuer progmem)
const char *p_argbuff
; // pointer auf den argbuffer
uint8_t fmtlen
, arglen
, overflow
;
uint8_t i
,j
,dec
;
uint8_t dstcnt
;
uint8_t minus
;
xprintf_t cmd
; // parser Daten
p_fmtbuff
= format
;
if( useprogmem
) // format von progmem in's RAM kopieren
{
strncpy_P
( pgmbuff
, format
, PGMBUFF_SIZE
);
pgmbuff
[PGMBUFF_SIZE
-1] = 0;
p_fmtbuff
= pgmbuff
;
}
cmd.
parse = false;
p_fmt
= p_fmtbuff
-1; // -1 -> wird am Anfang der Schleife korrigiert
p_dst
= buffer
;
dstcnt
= 0;
do
{
if( dstcnt
>= n
) // max. Anzahl von Zeichen fuer Ziel 'buffer' erreicht?
break;
p_fmt
++; // naechstes Zeichen von format
//########################
//# 1. PARSE
//########################
//------------------------
// START: parse cmd
//------------------------
if( cmd.
parse == false && *p_fmt
== '%' )
{
memset( &cmd
, 0, sizeof(xprintf_t
) ); // init
cmd.
parse = true;
cmd.
pad = ' ';
p_cmddigit
= cmddigit
;
continue;
}
//------------------------
// NO parse: copy char
//------------------------
if( cmd.
parse == false )
{
*p_dst
= *p_fmt
;
p_dst
++;
dstcnt
++;
continue;
}
//------------------------
// set: pad (eine '0' ganz am Anfang der Formatmaske)
//------------------------
if( cmd.
parse == true && *p_fmt
== '0' && p_cmddigit
== cmddigit
)
{
cmd.
pad = '0';
continue;
}
//------------------------
// set: vor/nach-kommastellen
//------------------------
if( cmd.
parse == true && *p_fmt
>= '0' && *p_fmt
<= '9' )
{
*p_cmddigit
= *p_fmt
;
p_cmddigit
++;
continue;
}
//------------------------
// set: point
//------------------------
if( cmd.
parse == true && *p_fmt
== '.' && cmd.
point == false )
{
cmd.
point = true;
*p_cmddigit
= 0;
cmd.
prelen = atoi( cmddigit
);
p_cmddigit
= cmddigit
;
continue;
}
//------------------------
// set: uselong
//------------------------
#ifdef USE_XPRINTF_LONG
if( cmd.
parse == true && *p_fmt
== 'l' )
{
cmd.
uselong = true;
continue;
}
#endif
//------------------------
// END: parse cmd
//------------------------
if( cmd.
parse == true && (*p_fmt
== 'd' || *p_fmt
== 'u' || *p_fmt
== 'x' || *p_fmt
== 'b' || *p_fmt
== 'o' ||
*p_fmt
== 's' || *p_fmt
== 'S' || *p_fmt
== 'c' || *p_fmt
== '%') )
{
cmd.
cmd = *p_fmt
;
cmd.
parse = false;
*p_cmddigit
= 0;
if( cmd.
point == false ) cmd.
prelen = atoi(cmddigit
);
else cmd.
declen = atoi(cmddigit
);
if( cmd.
point || cmd.
prelen>0 )
cmd.
mask = true;
}
//########################
//# 2. EXECUTE
//########################
//------------------------
// exec cmd: "d,u,x,b,o"
//------------------------
if( cmd.
cmd == 'd' || cmd.
cmd == 'u' || cmd.
cmd == 'x' || cmd.
cmd == 'b' || cmd.
cmd == 'o' )
{
if( cmd.
uselong )
{
#ifdef USE_XPRINTF_LONG
switch(cmd.
cmd)
{
case 'd': ltoa ( va_arg(ap
, long) , argbuff
, 10); break; // LONG dezimal int signed
case 'u': ultoa
( va_arg(ap
, unsigned long), argbuff
, 10); break; // LONG dezimal int unsigned
case 'x': ultoa
( va_arg(ap
, unsigned long), argbuff
, 16); break; // LONG hex int unsigned
case 'b': ultoa
( va_arg(ap
, unsigned long), argbuff
, 2); break; // LONG binary int unsigned
case 'o': ultoa
( va_arg(ap
, unsigned long), argbuff
, 8); break; // LONG octal int unsigned
}
#endif
}
else
{
switch(cmd.
cmd)
{
case 'd': itoa( va_arg(ap
, int) , argbuff
, 10); break; // dezimal int signed
case 'u': utoa
( va_arg(ap
, unsigned int), argbuff
, 10); break; // dezimal int unsigned
case 'x': utoa
( va_arg(ap
, unsigned int), argbuff
, 16); break; // hex int unsigned
case 'b': utoa
( va_arg(ap
, unsigned int), argbuff
, 2); break; // binary int unsigned
case 'o': utoa
( va_arg(ap
, unsigned int), argbuff
, 8); break; // octal int unsigned
}
}
minus
= (argbuff
[0] == '-');
arglen
= strlen(argbuff
);
fmtlen
= cmd.
prelen + cmd.
declen + (cmd.
point ? 1 : 0);
arglen
= strlen(argbuff
);
overflow
= cmd.
mask && // Zahl zu gross -> "*" anzeigen statt der Zahl
(arglen
> cmd.
prelen + cmd.
declen || cmd.
prelen < 1+minus
);
if( overflow
) // overflow: Zahl passt nicht in Maske
{ // -> zeige '*.*'
for( i
=0; (i
< fmtlen
) && (dstcnt
< n
); i
++)
{
if( cmd.
point && i
==cmd.
prelen ) *p_dst
= '.';
else *p_dst
= '*';
p_dst
++;
dstcnt
++;
}
}
else // else: if( overflow )
{
if( !cmd.
mask ) // keine Maske: alles von der Zahl ausgeben
fmtlen
= arglen
;
p_argbuff
= argbuff
;
if( minus
) // wenn Zahl negativ: merken und auf dem argbuff nehmen
{
p_argbuff
++;
arglen
--;
}
//-----------------
// die Zahl wird 'Rueckwaerts' uebertragen
//-----------------
dec
= -1; // wird am Anfang der Schleife auf 0 gesetzt
j
= 1; // zaehler fuer argbuff
for( i
=1; i
<=fmtlen
; i
++ )
{
dec
++; // Zaehler Dizmalstellen
if( dstcnt
+fmtlen
-i
<= n
) // wenn Zielbuffer nicht ueberschritten
{
if( cmd.
point && (dec
== cmd.
declen) ) // Dezimalpunkt setzen
{
p_dst
[fmtlen
-i
] = '.';
continue;
}
if( j
<= arglen
) // Ziffer uebertragen aus argbuff
{
p_dst
[fmtlen
-i
] = p_argbuff
[arglen
-j
];
j
++;
continue;
}
if( cmd.
declen > 0 && // Nachkomma und 1. Vorkommastelle ggf. auf '0'
(dec
< cmd.
declen || dec
== cmd.
declen+1) ) // setzen wenn die Zahl zu klein ist
{
p_dst
[fmtlen
-i
] = '0';
continue;
}
if( minus
&& ( (cmd.
pad == ' ') || // ggf. Minuszeichen setzen
(cmd.
pad != ' ' && i
== fmtlen
) ) ) // Minuszeichen bei '0'-padding an erster Stelle setzen
{
minus
= false;
p_dst
[fmtlen
-i
] = '-';
continue;
}
p_dst
[fmtlen
-i
] = cmd.
pad; // padding ' ' oder '0'
} // end: if( dstcnt+fmtlen-i <= n )
}
p_dst
+= fmtlen
;
dstcnt
+= fmtlen
;
}
continue;
}
//------------------------
// exec cmd: "s", "S", "c"
//------------------------
if( cmd.
cmd == 's' || cmd.
cmd == 'S' || cmd.
cmd == 'c' )
{
switch(cmd.
cmd)
{
case 's': p_str
= va_arg( ap
, char *); // String aus dem RAM
break;
case 'S': strncpy_P
( argbuff
, va_arg( ap
, char *), ARGBUFF_SIZE
); // String liegt im progmem -> in's RAM kopieren
argbuff
[ARGBUFF_SIZE
-1] = 0;
p_str
= argbuff
;
break;
case 'c': argbuff
[0] = va_arg( ap
, int); // einzelnes char
argbuff
[1] = 0;
p_str
= argbuff
;
break;
}
fmtlen
= cmd.
prelen;
arglen
= strlen(p_str
);
if( !cmd.
mask ) // keine Maske: alles vom String ausgeben
fmtlen
= arglen
;
for( i
=0; i
<fmtlen
; i
++)
{
if( dstcnt
< n
) // wenn Zielbuffer nicht ueberschritten
{
if( *p_str
) // char uebertragen
{
*p_dst
= *p_str
;
p_str
++;
}
else // padding
{
*p_dst
= ' ';
}
p_dst
++;
}
dstcnt
++;
}
continue;
}
//------------------------
// exec cmd: "%"
//------------------------
if( cmd.
cmd == '%' )
{
*p_dst
= '%';
p_dst
++;
dstcnt
++;
continue;
}
} while( (dstcnt
< n
) && *p_fmt
);
*(p_dst
+ (dstcnt
< n
? 0 : -1)) = 0; // terminierende 0 im Ausgabebuffer setzen
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
void xsnprintf
( char *buffer
, uint8_t n
, const char *format
, ...
)
{
va_list ap
;
va_start(ap
, format
);
_xvsnprintf
( false, buffer
, n
, format
, ap
);
va_end(ap
);
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
void xsnprintf_P
( char *buffer
, uint8_t n
, const char *format
, ...
)
{
va_list ap
;
va_start(ap
, format
);
_xvsnprintf
( true, buffer
, n
, format
, ap
);
va_end(ap
);
}
//-----------------------------------------------------------
// buffered_sprintf_P( format, ...)
//
// Ausgabe direkt in einen internen Buffer.
// Der Pointer auf den RAM-Buffer wird zurueckgegeben.
// Abgesichert bzgl. Buffer-Overflow.
//
// Groesse des Buffers: PRINTF_BUFFER_SIZE
//
// Parameter:
// format : String aus PROGMEM (siehe: xprintf in utils/xstring.h)
// ... : Parameter fuer 'format'
//-----------------------------------------------------------
char * buffered_sprintf
( const char *format
, ...
)
{
va_list ap
;
va_start( ap
, format
);
_xvsnprintf
( false, sprintf_buffer
, SPRINTF_BUFFER_SIZE
, format
, ap
);
va_end(ap
);
return sprintf_buffer
;
}
//-----------------------------------------------------------
// buffered_sprintf_P( format, ...)
//
// Ausgabe direkt in einen internen Buffer.
// Der Pointer auf den RAM-Buffer wird zurueckgegeben.
// Abgesichert bzgl. Buffer-Overflow.
//
// Groesse des Buffers: PRINTF_BUFFER_SIZE
//
// Parameter:
// format : String aus PROGMEM (siehe: xprintf in utils/xstring.h)
// ... : Parameter fuer 'format'
//-----------------------------------------------------------
char * buffered_sprintf_P
( const char *format
, ...
)
{
va_list ap
;
va_start( ap
, format
);
_xvsnprintf
( true, sprintf_buffer
, SPRINTF_BUFFER_SIZE
, format
, ap
);
va_end(ap
);
return sprintf_buffer
;
}
//--------------------------------------------------------------
// kopiert einen String von src auf dst mit fester Laenge und
// ggf. Space paddings rechts
//
// - fuellt ggf. den dst-String auf size Laenge mit Spaces
// - setzt Terminierung's 0 bei dst auf Position size
//--------------------------------------------------------------
void strncpyfill
( char *dst
, const char *src
, size_t size
)
{
uint8_t i
;
uint8_t pad
= false;
for( i
=0; i
<size
; i
++)
{
if(*src
== 0) pad
= true;
if( pad
) *dst
= ' ';
else *dst
= *src
;
src
++;
dst
++;
}
dst
--;
*dst
= 0;
}
//--------------------------------------------------------------
// entfernt rechte Leerzeichen aus einem String
//--------------------------------------------------------------
void strrtrim
( char *dst
)
{
char *p
;
p
= dst
+ strlen(dst
) - 1;
while( (p
!= dst
) && (*p
== ' ') ) p
--;
if( (*p
!= ' ') && (*p
!= 0) ) p
++;
*p
= 0;
}
//--------------------------------------------------------------
// INTERN - fuer strncpyat(), strncpyat_P()
//--------------------------------------------------------------
void _strncpyat
( char *dst
, const char *src
, size_t size
, const char sepchar
, uint8_t sepcharcount
, uint8_t progmem
)
{
uint8_t i
;
if( progmem
)
strncpy_P
( dst
, src
, size
);
else
strncpy( dst
, src
, size
);
for( i
=0; i
<size
; i
++)
{
if( *dst
== 0) return;
if( *dst
== sepchar
)
{
sepcharcount
--;
if( sepcharcount
==0 )
{
*dst
= 0;
return;
}
}
dst
++;
}
dst
--;
*dst
= 0;
}
//--------------------------------------------------------------
// strncpyat( dst, src, size, sepchar)
//
// kopiert einen String von 'src 'auf 'dst' mit max. Laenge 'size'
// oder bis 'sepchar' gefunden wird.
//
// src in PROGMEM
//--------------------------------------------------------------
void strncpyat
( char *dst
, const char *src
, size_t size
, const char sepchar
, uint8_t sepcharcount
)
{
_strncpyat
( dst
, src
, size
, sepchar
, sepcharcount
, false);
}
//--------------------------------------------------------------
// strncpyat_P( dst, src, size, sepchar)
//
// kopiert einen String von 'src 'auf 'dst' mit max. Laenge 'size'
// oder bis 'sepchar' gefunden wird.
//
// src in RAM
//--------------------------------------------------------------
void strncpyat_P
( char *dst
, const char *src
, size_t size
, const char sepchar
, uint8_t sepcharcount
)
{
_strncpyat
( dst
, src
, size
, sepchar
, sepcharcount
, true);
}
//--------------------------------------------------------------
// UTCdatetime2local( PKTdatetime_t *dtbuffer, PKTdatetime_t dt )
//
// konvertiert die UTC-Time 'dt' in die lokale Zeit und speichert
// dieses in 'dtbuffer' ab.
//
// Parameter:
//
// dtdst: Pointer Destination (PKTdatetime_t) (Speicher muss alloziiert sein!)
// dtsrc: Pointer Source (PKTdatetime_t)
//
// Hinweise:
//
// Schaltjahre (bzw. der 29.02.) werden nicht unterstuetzt
//--------------------------------------------------------------
void UTCdatetime2local
( PKTdatetime_t
*dtdst
, PKTdatetime_t
*dtsrc
)
{
int8_t timeoffset
;
int32_t v
;
int8_t diff
;
// 01 02 03 04 05 06 07 08 09 10 11 12 Monat
int8_t daymonth
[] = {31,28,31,30,31,30,31,31,30,31,30,31};
//--------------------------
// timezone: Einstellbereich -12 .. 0 .. 12 (+1 = Berlin)
// summertime: Einstellung: 0 oder 1 (0=Winterzeit, 1=Sommerzeit)
//--------------------------
timeoffset
= Config.
timezone + Config.
summertime;
//timeoffset = 2; // solange noch nicht in PKT-Config: Berlin, Sommerzeit
memcpy( dtdst
, dtsrc
, sizeof(PKTdatetime_t
) ); // copy datetime to destination
//--------------------------
// Zeitzonenanpassung
//--------------------------
if( dtdst
->year
!= 0 && dtdst
->month
>= 1 && dtdst
->month
<= 12 ) // nur wenn gueltiges Datum vorhanden
{
//--------------------------
// 1. Sekunden
//--------------------------
v
= (int32_t)dtdst
->seconds
;
v
+= timeoffset
*3600; // Stunden korrigieren
diff
= 0;
if( v
> 86400 ) // Tagesueberschreitung? (86400 = 24*60*60 bzw. 24 Stunden)
{
v
-= 86400;
diff
++; // inc: Tag
}
else if( v
< 0 ) // Tagesunterschreitung?
{
v
+= 86400;
diff
--; // dec: Tag
}
dtdst
->seconds
= (uint32_t)v
; // SET: seconds
//--------------------------
// 2. Tag
//--------------------------
v
= (int32_t)dtdst
->day
;
v
+= diff
;
diff
= 0;
if( v
> daymonth
[dtdst
->month
-1] ) // Monatsueberschreitung?
{
v
= 1; // erster Tag des Monats
diff
++; // inc: Monat
}
else if( v
< 1 ) // Monatsunterschreitung?
{
if( dtdst
->month
> 1 )
v
= daymonth
[dtdst
->month
-1-1]; // letzter Tag des vorherigen Monats
else
v
= 31; // letzter Tag im Dezember des vorherigen Jahres
diff
--; // dec: Monat
}
dtdst
->day
= (uint8_t)v
; // SET: day
//--------------------------
// 3. Monat
//--------------------------
v
= (int32_t)dtdst
->month
;
v
+= diff
;
diff
= 0;
if( v
> 12 ) // Jahresueberschreitung?
{
v
= 1;
diff
++; // inc: Jahr
}
else if( v
< 1 ) // Jahresunterschreitung?
{
v
= 12;
diff
--; // dec: Jahr
}
dtdst
->month
= (uint8_t)v
; // SET: month
//--------------------------
// 4. Jahr
//--------------------------
dtdst
->year
+= diff
; // SET: year
}
}