Encoding number to date time - c

I have 32 bit encoded value for date and time value.I know the sample 32bit encoded value date and time but i don't know how to convert and get this value using c program or any other language or script. The sample data as below
54 FE C0 72 =(25-Oct-13 20:34:58)
55 01 DC 8B =(26-Oct-13 22:34:51)
57 01 DC 8B =(14-Apr-14 14:34:51)
42 23 8F 96 =(02-Jun-09 17:06:30)
3C F5 28 4B= (31-Mar-00 18:51:55)
3A F4 28 49 =(12-Oct-99 18:51:53)
For the above sample data i am tried using unix timestamp method but i am getting wrong date time value.The answers using unix time stamp method as below
54 FE C0 72 =(10 Mar 2015 09:59:14 GMT
55 01 DC 8B =(12 Mar 2015 18:35:55 GMT)
57 01 DC 8B =(04 Apr 2016 03:16:27 GMT)
Please give me your guidance to convert the above 32bit encoded value to correct date and time value. And please share any other methods other than unix timestamp.And i think some algorithms are encrypted inside the hex code
EDIT:
Adding examples from chat:
30067004 =24-mar-1997 07:57:56
2C067004=Tue Apr 16 23:57:56 1996
29567004=Thu Aug 31 15:57:56 1995
13567004=9-jul-90 7:57:56
17567004=15-jun-91 15:57:56
1C567004=15-aug-92 07:57:56
20567004=23-jul-93 15:57:56
24867004=15-jul-94 23:57:56
10067004=29 sep-89 15:57:56
2B067004=21-jan-96 15:57:56
00000000 = 1-Jan-1986
00030000 = 1-jan-1986
00038000 = 1-jan-1994

Let's start by looking at the first two:
54 FE C0 72 =(25-oct-13 20:34:58)
54 FE C0 78 =(25-oct-13 20:35:04)
These two dates are 6 seconds apart, and the values differ by 6. So we know that at least the last byte specifies seconds.
55 01 DC 8B =(26-oct-13 22:34:51)
55 01 E3 93 =(26-oct-13 23:04:51)
Similarly, these two are are 30 minutes (1800 seconds) apart, and the values differ by 1800. So at least the last two bytes specify seconds.
54 FE C0 78 =(25-oct-13 20:35:04)
55 01 DC 8B =(26-oct-13 22:34:51)
There's a larger range in the values, but note that the last two bytes seem to be fairly close in value. Taking DC8Bh - C078h gives us 1C13h = 7187d. That's a difference of 2 hours (7200 seconds) minus 13 seconds, which is how far apart the time portions of the two dates are. So it looks like the last two bytes specify the time. However, there are 86400 seconds in a day, and C078h = 49272d which would be closer to around 13:00:00 than 20:35:04, that and the largest value you can store in 16 bits is 65535. Also, the first two bytes differ by 3 but the dates differ by 1. Let's come back to that in a bit.
55 01 DC 8B =(26-oct-13 22:34:51)
57 01 DC 8B =(14-Apr-14 14:34:51)
Note here that the last two bytes are the same, and that the minutes and seconds are the same, although the hours differ by 8. So perhaps the last two bytes specify seconds in part of a day. Going back to the prior example, the first two bytes differed by 3 when the dates differed by 1. So perhaps the first two bytes specify an 8 hour interval. This would account for the last two bytes being the same when the time differs by 8 or 16 hours. If we take 5701h - 5501h we get 200h = 512d. Dividing by 3 we get 169 2/3 days. The two dates above differ by 170 days, and if you take the hours into account it's about 169 2/3.
So now we have dates. 5501h is the third 8-hour interval in 26-oct-13, so the start of the day is 54FFh = 21759d. Dividing by 3 gives us 7253. Counting back days, that gives us an epoch date of 1993-12-17.
Now lets go back to the time. Assuming the last two bytes are seconds in an 8-hour interval. That gives us a maximum value of 28800d. Note that this value only needs 15 bits to store. DC8Bh has the highest bit set, so let's see what we get if we mask that bit out. That gives us 5C8Bh = 23691d, and 23691 seconds is 6 hours 34 minutes 51 seconds. That matches the third and fifth examples with a difference of 8 hours.
As for the most significant bit in the third byte, my guess is that is specifies DST. All the examples have this bit set, and all the dates are when DST is active.
So to summarize:
The first two bytes divided by 3 is number of days since 1993-12-17.
The first two bytes mod 3 is the 8-hour interval in the day. Multiply this value by 28800 (i.e. seconds in 8 hours) to set the initial time in seconds.
The last two bytes with the most significant bit masked out are seconds from the start of the 8-hour interval. Add this value to the value from the prior step to get seconds from midnight.
Check the most significant bit in the third byte to set the DST flag.
EDIT:
So it seems the result this algorithm gives for 57 01 DC 8B =(14-Apr-14 14:34:51) is ahead by one day. Let's look at one of the new examples:
42 23 8F 96 =(02-Jun-09 17:06:30)
Our algorithm give a date of 30-May-09, so it's behind by 3 days. This is interesting because it differs from what we got for 25-Oct-13 and 26-Oct-13 by about 4 years. What's different is that there's a leap year in between. So perhaps this encoding is assuming all years have 366 days. If we go back to the epoch date of 1993-12-17, we see that there are 15 non-leap years from 1994 to 2013 inclusive. That give us a new epoch date of 1994-1-1, which makes more sense. So after doing the initial conversion with 1994-1-1 as the epoch, we need to count the number of non-leap years and subtract that many days.
Now let's look at this one:
3A F4 28 49 =(12-Oct-99 18:51:53)
The time is still correct, but the date is way off. Notice however that the most significant bit of byte 3 is NOT set. This seems to indicate a different epoch. The start of 12-Oct-99 is 3AF2h = 15090d. Dividing by 3 gives us 5030. Counting backward gives us an epoch of 1986-01-03. But then there's this:
00000000 = 1-Jan-1986
00030000 = 1-jan-1986
So it looks like 1986-1-1 is the epoch, but there's a special case in place for this date, so the actual epoch is 1985-12-31.
But, we're off by 3 days. If all years had 366 days, this would not be the case. It would work however if all years had 365 days. This means that for the 1985-12-31 epoch, we need to count the number of leap years and add that many days. This is the opposite of what we need to do with the 1994-1-1 epoch.
This now works for almost everything. Everything except these:
3C F5 28 4B= (31-Mar-00 18:51:55)
1C567004=15-aug-92 07:57:56
But it does work for this:
2B067004=21-jan-96 15:57:56
So it looks like this encoding does do a leap year check, but only for the current year.
Taking these changes to the algorithm and applying them to the code provided by LPs, we now have this:
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
int isleap(int year)
{
if (year % 4 != 0) return 0;
if (year % 100 != 0) return 1;
if (year % 400 != 0) return 0;
return 1;
}
int main(int argc, char *argv[])
{
// Read encoding from command line
uint32_t datetime = strtoul(argv[1],NULL,16);
uint16_t mydate = datetime >> 16;
uint16_t mytime = datetime & 0xFFFF;
int new_encoding = (mytime & 0x8000) != 0;
// Calculate days
time_t linuxSeconds = (mydate/3);
// Calculate the 8 hours on current day of date
uint8_t third_Count = mydate % 3;
// Add days from 1/1/1970, that is the base of time in linux
if (new_encoding)
{
// Days between 1970-1-1 and 1994-1-1 minus 1
linuxSeconds += 8765;
}
else
{
// Days between 1970-1-1 and 1986-1-1 minus 1
linuxSeconds += 5843;
}
// Calculate total amount of hours
linuxSeconds *= 24;
// Calculate total amount of seconds
linuxSeconds *= 3600;
// Add seconds of last 8 hours group
linuxSeconds += (mytime & 0x7FFF);
// Add alla seconds of grups of 8 hours of date
linuxSeconds += (third_Count * 28800);
// Add or subtract days depending on whether new_encoding is set
struct tm *mytm = gmtime(&linuxSeconds);
int daydiff = 0, year;
for (year = new_encoding ? 1994 : 1986; year <= mytm->tm_year + 1900; year++) {
if (year < mytm->tm_year + 1900) {
if (new_encoding) {
// remove a day for non-leap years
if (!isleap(year)) {
daydiff--;
}
} else {
// add a day for leap years unless it's the current year
if (year != (mytm->tm_year + 1900) && isleap(year)) {
daydiff++;
}
}
}
}
if (mydate < 0x0003) {
// special case for day 0
linuxSeconds += 86400;
} else {
linuxSeconds += daydiff * 86400;
}
// Print the date with actual GMT
printf(ctime(&linuxSeconds));
// Print Greenwich time
printf(asctime(gmtime(&linuxSeconds)));
return 0;
}
One caveat about this code: if it's run on a system where time_t is 32-bit, it won't be able to properly display dates after 2038. If time_t is 64-bit, those dates will display properly.
EDIT 2:
There was an issue with code 30068000 being one day ahead. There was a bug in the code when checking the current month. The tm_mon field in struct tm ranges from 0 to 11, not 1 to 12. Fixed.
EDIT 3:
So it seems the month/day check when adding/subtracting days was just plain wrong, as it was causing Feb 28 to appear twice. When I removed that, I found that the 1994 scheme was a day ahead, so it looks like it has the same special case for day 0 that the 1986 scheme has. Fixed again.

Using the perfect explanation of #dbush, below you can find a simple linux gcc compiled code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
void decrypt ( uint32_t data )
{
uint16_t mydate = data>>16;
uint16_t mytime = data&0x0000FFFF;
// Calculate days
time_t linuxSeconds = (mydate/3);
// Calculate the 8 hours on current day of date
uint8_t third_Count = mydate % 3;
// Add days from 1/1/1970, that is the base of time in linux
if (mytime&0x8000)
{
// Days from 171/1970 to 17/12/1993
linuxSeconds += 8751;
}
else
{
// Days from 171/1970 to 19/03/1984
linuxSeconds += 5846;
}
// Calculate total amount of hours
linuxSeconds *= 24;
// Calculate total amount of seconds
linuxSeconds *= 3600;
// Add seconds of last 8 hours group
linuxSeconds += (mytime&0x7FFF);
// Add alla seconds of grups of 8 hours of date
linuxSeconds += (third_Count * 28800);
// Print the date with actual GMT
// printf(ctime(&linuxSeconds));
// Print Greenwich time
printf(asctime(gmtime(&linuxSeconds)));
}
int main(int argc, char *argv[])
{
decrypt(0x54FEC072);
decrypt(0x5501DC8B);
decrypt(0x5701DC8B);
decrypt(0x42238F96);
decrypt(0x3CF5284B);
decrypt(0x3AF42849);
return 0;
}
As #rmrps pointed out the last example is out of 1 day.
OUTPUT OF THE EXAMPLE ABOVE
Fri Oct 25 20:34:58 2013
Sat Oct 26 22:34:51 2013
Tue Apr 15 14:34:51 2014
Sat May 30 17:06:30 2009
Fri Mar 31 18:51:55 2000
Tue Oct 12 18:51:53 1999

Building on dbush' excellent analysis, there still one issue regarding the days.
This can be explained by the format, instead of counting days since epoch counts years since epoch and day of year, but ignoring leap years and using a fixed 366 day year. This sets the epoch to 31 December 1993.
So from the days_since_epoch part in dbush' answer, calculate years_since_epoch = days_since_epoch / 366 and day_of_year = days_since_epoch % 366. Set the epoch at 1993-12-31, and add the lapsed time since epoch (taking leap days into account), and you'll get the correct dates from the timestamp.
The last two timestamps you added doesn't fit dbush' analysis and so seem follow a different format. Note that the bit that dbush assumed was a DST indicator is no longer set - it actually appears to be a date scheme selector rather than DST indicator.
The date part of the last two examples is simpler - it's simply days since epoch (but a different epoch - 3 January 1986).
Edit: following discussion in chat, I'm updating my code samples.
Because my implementation - and some of the results differ from dbush' implementation and results, I'm adding an explanation as well.
If the format is based on 4 hex bytes AA BB CC DD, we treat this as a big-endian 32-bit number (AA msb = bit 31, DD lsb = bit 0), the hex timestamp is decoded as follows:
bits 0-14: number of seconds into a 8-hour window
bit 15: date scheme/epoch selector (see below)
bits 31-16 mod 3: the 8-hour window of the day.
bits 31-16 div 3: an indication of days since epoch (let's call it day_count)
day_count is not the actual number of days as both schemes treats every year as having a fixed number of days. It also includes the current (partial) day, so we should remove the partial day by subtracting one from day_count. This also means that a zero day_count is probably invalid (This has been confirmed for one of the two schemes 00000000 and 00030000 yield the same date - if 00008000 and 00038000 yield the same value it also holds for the other scheme)
For each scheme, there are only two parameters that differ between the schemes, epoch and days_per_year. Given these parameters, the calculation is the same - work out the following:
years_since_epoch = day_count / days_per_year
days_since_new_year = day_count % days_per_year // whole days
Then calculate the actual number of days since epoch as days_since_new_year plus the number of days in each year since epoch, taking leap days into account.
The two schemes are selected by bit 15:
If 1, epoch is 1994-01-01 and days_per_year is 366
If 0, epoch is 1986-01-01 and days_per_year is 365
The following code decodes both timestamp formats:
#include <stdint.h>
#include <time.h>
int isLeapYear( int y )
{
if ( y % 400 == 0 ) return 1;
if ( y % 100 == 0 ) return 0;
if ( y % 4 == 0 ) return 1;
return 0;
}
time_t decodeTimestamp (uint32_t timestamp)
{
time_t result = 0;
int y, days_since_new_year, years_since_epoch, epoch_year;
int day_count = (timestamp >> 16 ) /3;
int part_of_day = (timestamp >> 16 ) %3;
int seconds_in_day = part_of_day * 8 * 3600 + ( (timestamp & 0x7FFF) % 28800 ) ;
if ( day_count > 0 )
{
--day_count; // remove current (partial) day from day_count
}
if ( ((timestamp >> 15) & 1) == 1 ) // bit 15 is date scheme
{
days_since_new_year = day_count % 366;
years_since_epoch = day_count / 366;
epoch_year = 1994;
result = 757382400; //1994-01-01
}
else
{
days_since_new_year = day_count % 365;
years_since_epoch = day_count / 365;
epoch_year = 1986;
result = 504921600;//1986-01-01
}
result += years_since_epoch * 365 *24*60*60;
for ( y = epoch_year ; y < epoch_year + years_since_epoch; ++y )
{
if ( isLeapYear( y ) )
{
result += 24 * 60 * 60;
}
}
result += days_since_new_year * 24 * 60 * 60;
result += seconds_in_day;
return result;
}
I've put this up on http://codepad.org/K4JC0zmf, which also includes a main function which test against all of the examples I've seen in this thread. The only one it falls over is C0068000, which is explained by time_t being 32 bits not 64 bits.
Edit 2: Updated dbush' implementation in the side-by-side on codepad.
I've also put both mine and dbush' (current) implementation on http://codepad.org/vMYzNM4g to see the differences.
Both methods give the same correct results, apart from the 2038 overflow case (C0068000). I expect that they would give the same result with a 64-bit time_t however, so I think both algorithms are now correct.

E393 - DC8B = 1800
23:04:51 - 22:34:51 = 1800s
(10 Mar 2015 09:59:14 GMT - 25-oct-13 20:34:58) = 43248256 seconds
Try subtracting 43248256 decimal (293EA80 hex) from the number and then use it as if it was a unix time stamp:
54FEC072h - 293EA80h = 526AD5F2h = 1382733298d = "Fri, 25 Oct 2013 20:34:58 GMT"

Related

Matlab: Daily 3d array to monthly- dealing with alternate days in a month and leap years - How to do it?

In matlab I have a 720x360x365 matrix (let's call it A) of daily precipitation for one year. 365 stands for days in a year. I need to write a code to convert these daily data to the monthly sum. If I start from January, I need to do mean (A,3) of the first 31 days, then the mean (A,3) of February, the next 28 or 29 days. Because the days alternate between 31 and 30 (and 28 or 29 for February), I don't know how to write a code to do this.
please help me I don't know how to do it.
thank you
You can use mat2cell to divide your data in cells per month. First make a vector with the number of days per month (not taking into account leap years), and then use this to divide the data. Then you can use cellfun on each cell (i.e. month) to get any metric you define per month:
data = rand(720, 360, 365);
days_per_month = [31 28 31 30 31 30 31 31 30 31 30 31];
% divide months in cells
data_cell = mat2cell(data, size(data,1), size(data,2), days_per_month);
mean_cell = cellfun(#(A) mean(A,3), data_cell, 'UniformOutput', false)
To use this in a loop, and account for leap years, you can use the function leapyear(year):
days_per_month = [31 28 31 30 31 30 31 31 30 31 30 31];
years = 1984:2015
for k = 1:numel(years)
if leapyear(years(k))
days_per_month(2) = 29;
else
days_per_month(2) = 28;
end
% rest of what you want to do
end

Finding difference between two dates using mktime()

I am trying to find the difference between two dates in seconds.
I have used tm1 and tm2 to store the two different dates.
tm1 = 1900-01-01 00:00:00
tm2 = 2000-01-01 20:38:40
 
tm1.tm_year = 0
tm1.tm_mon = 0
tm1.tm_mday = 1
 
tm2.tm_year = 100
tm2.tm_mon = 0
tm2.tm_mday = 1
tm2.tm_hour = 20
tm2.tm_min = 38
tm2.tm_sec = 40
Now to the code :
time_t t1, t2;
t1=mktime(&tm1);
t2=mktime(&tm2);
int diff = difftime(t2,t1);
The actual value of t1 in this case is 2209010000 and that of t2 is 946739320.
Now I executed a similar function using PostgreSQL :
select EXTRACT(EPOCH FROM ('2000-01-01 20:38:40' - CAST('1900-01-01' AS TIMESTAMP)));
But the result from difftime() function is 3155749320 and the result through querying is 3155747920, a difference of 1400 seconds.
Why is there a difference in the result obtained from both the methods? What would have caused the difference? Do I need to handle time zones?
The unix timestamp for a given date begins at 1/1/1970, so the first thing that hits me when you show your timestamps is that 1/1/1900 should be negative. If we analize the exact UNIX timestamp for the date you used, it should be -2208988800 (for 1/1/1900 00:00:00 UTC) which is larger (in absolute value) than the lowest minimum integer representable in 32 bits (in two's complement) which is -2147483648.
If you want to do exact calculations, you have to switch to 64bit integers, and you'll get the correct result.
Unix timestamp for 2000-01-01 20:38:40 -> 946759120
Unix timestamp for 1900-01-01 00:00:00 -> -2208988800
difference between the two: 946759120 - (-2208988800) = 3155747920 s.
The timestamp you obtained are the 32 least signifiant bits of the overflown result. The minimum date possible with 32 bit timestamps in two's complement is for -2147483648 -> 13/Dec/1901, 20:45:52.

Get the number of days of the current date with Time

I'm trying to get the number of days untill today. Like:
int days, seconds;
seconds = Time(0); // Get the number of SECONDS from January, 1º 1970 untill now.
days = seconds / (60 * 60 * 24);
printf("%d", days);
The output is: 16326.
But when I use some website that makes the conversion for you, they show 6464 days instead.
What am I doing wrong ?
You are right. There are 30 + 14 = 44 years, which gives about 16000 days.

How to check if week number is even or odd in ANSI C?

I`ve a small app that returns is week even or not.
time_t now = time(0);
tm *ltm = localtime(&now);
int twin=(ltm->tm_yday/7)%2
But independently from the 1st day of the year so it returns
mon, thu, we, etc
0,1,1,1,1,1,1
in the next week
1,0,0,0,0,0,0
In the next year
mon, thu, we, etc
0,0,1,1,1,1,1
in the next week
1,1,0,0,0,0,0
and so on..
Twin- if number modulo 2 = 0
So I have to add shift to change week number in each sunday or monday. Any suggestions?
You are assuming that first week has exactly 7 days which is incorrect.
For example Jan 1st 2013 was Tuesday, so the first week is only 5 days long.
How about using strftime? Something like:
time_t now = time(0);
tm *ltm = localtime(&now);
char weekNr[3];
strftime(weekNr, sizeof(weekNr), "%W", ltm);
int isOdd = atoi(weeknr) % 2;
What you call twin, in English is usually called even.
About your question, the issue here is that you are not calculating the week number correctly: you are simply dividing by 7, and that's not enough because the start of year and the start of week vary each year.
Moreover, there are several different ways to decide which one is week 1. See for example this code, to get started.
UPDATE: Copying shamelessly from the eglibc source code:
1) The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01 (strftime("%U")):
tp->tm_yday - tp->tm_wday + 7) / 7
2) The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01 (strftime("%W")):
(tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7
3) The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year (strftime("%V")):
Well this is complicated... so you are better with the idea by #MaikuMori of using strftime``, but with"%V", and then parse the result, withatoi()`.

Converting human time to epoch Unix time returning negative number

I am currently working on some C code and I am trying to convert a human readable date into an epoch time stamp (unix timestamp). However, its always returning a negative number. I'm using struct tm and hard coding the values of the date until I get it working properly. Below is the code
struct tm t;
time_t t_of_day;
t.tm_year = 2012 - 1970;
t.tm_mon = 9;
t.tm_mday = 24;
t.tm_hour = 11;
t.tm_min = 34;
t.tm_sec = 30;
t.tm_isdst = 1;
t_of_day = mktime(&t);
printf("Epoch time stamp is: %ld\n", t_of_day);
When this code executes I get the output of -858000330.
Thanks for any help you can provide.
2012 - 1970 computes to 42. And year 1942 is before 1/1/1970. That is normal that mktime() result into a negative timestamp though.
from mktime man page:
tm_year The number of years since 1900.
change your year calculation to 2012 - 1900 and you should be fine.
Please read the man pages, prior to coming here:
Verbatim from man mktime():
The members of the tm structure are:
tm_sec The number of seconds after the minute, normally in the range 0 to 59, but can be up to 60 to allow for leap seconds.
tm_min The number of minutes after the hour, in the range 0 to 59.
tm_hour The number of hours past midnight, in the range 0 to 23.
tm_mday The day of the month, in the range 1 to 31.
tm_mon The number of months since January, in the range 0 to 11.
tm_year The number of years since 1900.
tm_wday The number of days since Sunday, in the range 0 to 6.
tm_yday The number of days since January 1, in the range 0 to 365.
tm_isdst A flag that indicates whether daylight saving time is in effect at the time described. The value is positive if daylight saving time is in effect, zero if it is not, and negative if the information is not available.
tm_year is the number of years since 1900, not 1970.

Resources