mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
sys/phydat: add functions for Unix time conversion to phydat
Add functionality for calculating Unix timestamps to phydat. This allows one to convert dates received from RTCs and the like to Unix timestamps.
This commit is contained in:
parent
54544c0a36
commit
26653dd446
@ -288,6 +288,35 @@ void phydat_fit(phydat_t *dat, const int32_t *values, unsigned int dim);
|
||||
*/
|
||||
size_t phydat_to_json(const phydat_t *data, size_t dim, char *buf);
|
||||
|
||||
/**
|
||||
* @brief Convert a date and time contained in phydat structs to a Unix timestamp.
|
||||
* See phydat_unix() for the date notation and peculiarities.
|
||||
*
|
||||
* @param date Date to use in the timestamp.
|
||||
* @param time Time to use in the timestamp.
|
||||
* @param offset_seconds Timezone offset in seconds to use in the timestamp.
|
||||
*
|
||||
* @return A unix timestamp
|
||||
*/
|
||||
int64_t phydat_date_time_to_unix(phydat_t *date, phydat_t *time, int32_t offset_seconds);
|
||||
|
||||
/**
|
||||
* @brief Convert a date and time (per ISO8601) to a Unix timestamp (seconds since 1970).
|
||||
*
|
||||
* @param year Year in the Common Era (CE). Note that 0 is 1 BCE, 1 is 2 BCE, etc.
|
||||
* @param month Month of the year.
|
||||
* @param day Day of the month.
|
||||
* @param hour Hour of the day.
|
||||
* @param minute Minute of the hour.
|
||||
* @param second Second of the minute.
|
||||
* @param offset Timezone offset in seconds.
|
||||
*
|
||||
* @return A Unix timestamp (seconds since 1970).
|
||||
*/
|
||||
int64_t phydat_unix(int16_t year, int16_t month, int16_t day,
|
||||
int16_t hour, int16_t minute, int16_t second,
|
||||
int32_t offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
67
sys/phydat/phydat_unix.c
Normal file
67
sys/phydat/phydat_unix.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Silke Hofstra
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "phydat.h"
|
||||
|
||||
/**
|
||||
* Offsets of the first day of the month starting with January.
|
||||
* Months after February have a negative offset to efficiently handle leap years.
|
||||
*/
|
||||
static int16_t month_to_yday[] = { 0, 31, -306, -275, -245, -214,
|
||||
-184, -153, -122, -92, -61, -31 };
|
||||
|
||||
static inline int16_t phydat_unscale(int16_t value, int16_t scale)
|
||||
{
|
||||
if (scale > 0) {
|
||||
return value * pow(10, scale);
|
||||
}
|
||||
|
||||
if (scale < 0) {
|
||||
return value / pow(10, -scale);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int64_t phydat_date_time_to_unix(phydat_t *date, phydat_t *time, int32_t offset_seconds)
|
||||
{
|
||||
return phydat_unix(
|
||||
phydat_unscale(date->val[2], date->scale),
|
||||
phydat_unscale(date->val[1], date->scale),
|
||||
phydat_unscale(date->val[0], date->scale),
|
||||
phydat_unscale(time->val[2], time->scale),
|
||||
phydat_unscale(time->val[1], time->scale),
|
||||
phydat_unscale(time->val[0], time->scale),
|
||||
offset_seconds);
|
||||
}
|
||||
|
||||
int64_t phydat_unix(int16_t year, int16_t month, int16_t day,
|
||||
int16_t hour, int16_t minute, int16_t second,
|
||||
int32_t offset_seconds)
|
||||
{
|
||||
/* Make the year relative to 1900. */
|
||||
/* Add a year for months after Feb to deal with leap years. */
|
||||
year += (month > 2 ? 1 : 0) - 1900;
|
||||
|
||||
/* Calculate the day of the year based on the month */
|
||||
day += month_to_yday[(month - 1) % 12] - 1;
|
||||
|
||||
/* POSIX calculation of a UNIX timestamp. */
|
||||
/* See section 4.16 of The Open Group Base Specifications Issue 7. */
|
||||
int16_t leap_days = ((year - 69) / 4) - ((year - 1) / 100) + ((year + 299) / 400);
|
||||
|
||||
return (int64_t)(day + leap_days) * 86400 +
|
||||
(int64_t)(year - 70) * 31536000 +
|
||||
(int64_t)(hour) * 3600 +
|
||||
(int64_t)(minute) * 60 +
|
||||
(int64_t)(second) -
|
||||
offset_seconds;
|
||||
}
|
6
tests/phydat_unix/Makefile
Normal file
6
tests/phydat_unix/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += phydat
|
||||
USEMODULE += embunit
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
10
tests/phydat_unix/Makefile.ci
Normal file
10
tests/phydat_unix/Makefile.ci
Normal file
@ -0,0 +1,10 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
nucleo-l011k4 \
|
||||
samd10-xmini \
|
||||
stm32f030f4-demo \
|
||||
#
|
2
tests/phydat_unix/app.config.test
Normal file
2
tests/phydat_unix/app.config.test
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_MODULE_PHYDAT=y
|
||||
CONFIG_MODULE_EMBUNIT=y
|
213
tests/phydat_unix/main.c
Normal file
213
tests/phydat_unix/main.c
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Silke Hofstra
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Phydat Unix timestamp tests
|
||||
*
|
||||
* @author Silke Hofstra <silke@slxh.eu>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "phydat.h"
|
||||
#include "embUnit.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef PRIi64
|
||||
#define PRIi64 "lli"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
phydat_t date;
|
||||
phydat_t time;
|
||||
int32_t offset;
|
||||
int64_t ts;
|
||||
} test_t;
|
||||
|
||||
static test_t tests[] = {
|
||||
/* Test various ways of writing 0 */
|
||||
{
|
||||
.date = { { 1, 1, 1970 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 0,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 1, 1970 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 2 }, UNIT_TIME, 0 },
|
||||
.offset = 7200, /* UTC +0200 */
|
||||
.ts = 0,
|
||||
},
|
||||
{
|
||||
.date = { { 31, 12, 1969 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 22 }, UNIT_TIME, 0 },
|
||||
.offset = -7200, /* UTC -0200 */
|
||||
.ts = 0,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 1, 1970 }, UNIT_DATE, 0 },
|
||||
.time = { { 3600, 60, 0 }, UNIT_TIME, 0 },
|
||||
.offset = 7200, /* UTC +0200 */
|
||||
.ts = 0,
|
||||
},
|
||||
{
|
||||
.date = { { 31, 12, 1969 }, UNIT_DATE, 0 },
|
||||
.time = { { 3600, 120, 19 }, UNIT_TIME, 0 },
|
||||
.offset = -7200, /* UTC -0200 */
|
||||
.ts = 0,
|
||||
},
|
||||
|
||||
/* Test well-known dates */
|
||||
{
|
||||
.date = { { 28, 4, 2021 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 1619568000,
|
||||
},
|
||||
{
|
||||
.date = { { 29, 2, 2020 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 1582934400,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 3, 2020 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 1583020800,
|
||||
},
|
||||
|
||||
/* Test the first of every month */
|
||||
{
|
||||
.date = { { 1, 1, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2208988800,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 2, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2206310400,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 3, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2203891200,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 4, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2201212800,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 5, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2198620800,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 6, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2195942400,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 7, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2193350400,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 8, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2190672000,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 9, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2187993600,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 10, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2185401600,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 11, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2182723200,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 12, 1900 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = -2180131200,
|
||||
},
|
||||
|
||||
/* Test scale correction */
|
||||
{
|
||||
.date = { { 1, 1, 197 }, UNIT_DATE, 1 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 24364800,
|
||||
},
|
||||
{
|
||||
.date = { { 10, 10, 19700 }, UNIT_DATE, -1 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 0,
|
||||
},
|
||||
{
|
||||
.date = { { 1, 1, 1970 }, UNIT_DATE, 0 },
|
||||
.time = { { 36, 0, 0 }, UNIT_TIME, 2 },
|
||||
.ts = 3600,
|
||||
},
|
||||
|
||||
/* An invalid date that might go out of bounds on the day of the year lookup table */
|
||||
{
|
||||
.date = { { 1, 13, 1969 }, UNIT_DATE, 0 },
|
||||
.time = { { 0, 0, 0 }, UNIT_TIME, 0 },
|
||||
.ts = 0,
|
||||
},
|
||||
};
|
||||
|
||||
void test_phydat_date_time_to_unix(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
int64_t result = phydat_date_time_to_unix(
|
||||
&(tests[i].date), &(tests[i].time), tests[i].offset);
|
||||
|
||||
int32_t offset_hours = tests[i].offset / 3600;
|
||||
int32_t offset_minutes = (tests[i].offset % 3600) / 60;
|
||||
|
||||
DEBUG("Datetime: %04" PRIi16 "-%02" PRIi16 "-%02" PRIi16 "e%" PRIi16 " "
|
||||
"%02" PRIi16 ":%02" PRIi16 ":%02" PRIi16 "e%" PRIi16 " "
|
||||
"%+03" PRIi32 ":%02" PRIi32 " -> %" PRIi64 "\n",
|
||||
tests[i].date.val[2], tests[i].date.val[1], tests[i].date.val[0], tests[i].date.scale,
|
||||
tests[i].time.val[2], tests[i].time.val[1], tests[i].time.val[0], tests[i].time.scale,
|
||||
offset_hours, offset_minutes, result);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(tests[i].ts, result);
|
||||
}
|
||||
}
|
||||
|
||||
Test *tests_phydat_unix(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_phydat_date_time_to_unix),
|
||||
};
|
||||
EMB_UNIT_TESTCALLER(senml_tests, NULL, NULL, fixtures);
|
||||
return (Test *)&senml_tests;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TESTS_START();
|
||||
TESTS_RUN(tests_phydat_unix());
|
||||
TESTS_END();
|
||||
return 0;
|
||||
}
|
14
tests/phydat_unix/tests/01-run.py
Executable file
14
tests/phydat_unix/tests/01-run.py
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2017 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import sys
|
||||
from testrunner import run_check_unittests
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run_check_unittests())
|
Loading…
Reference in New Issue
Block a user