Packages

Erlang library for conversion from one local time to another based on google cctz

Current section

Files

Jump to
elocaltime c_src elocaltime.cc
Raw

c_src/elocaltime.cc

#include "elocaltime.h"
#include "elocaltime_nif.h"
#include "macros.h"
#include "nif_utils.h"
#include "cctz/time_zone.h"
#include <chrono>
#include <memory>
#include <string.h>
namespace {
cctz::time_zone kUtcTz = cctz::utc_time_zone();
const char kUnknownTimezone[] = "unknown timezone";
std::chrono::system_clock::time_point timestamp2tp(uint64_t ts)
{
std::chrono::duration<uint64_t> sec(ts);
return std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::system_clock::duration>(sec));
}
uint64_t tp2timestamp(const std::chrono::system_clock::time_point& tp)
{
return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
}
ERL_NIF_TERM kind2atom(cctz::time_zone::civil_lookup::civil_kind kind)
{
switch (kind)
{
case cctz::time_zone::civil_lookup::UNIQUE:
return ATOMS.atomCivilKindUnique;
case cctz::time_zone::civil_lookup::REPEATED:
return ATOMS.atomCivilKindRepeated;
case cctz::time_zone::civil_lookup::SKIPPED:
return ATOMS.atomCivilKindSkipped;
default:
return ATOMS.atomUndefined;
}
}
class TimeZoneWrapper
{
public:
TimeZoneWrapper() {}
~TimeZoneWrapper() {}
bool load(const std::string& timezone);
void load_utc_timezone();
void load_local_timezone();
void load_fixed_timezone(int32_t offset);
std::string format(const std::string& format, std::chrono::system_clock::time_point tp);
cctz::time_zone::civil_lookup lookup(const cctz::civil_second& sec);
cctz::time_zone::absolute_lookup lookup(uint64_t seconds);
private:
DISALLOW_COPY_AND_ASSIGN(TimeZoneWrapper);
cctz::time_zone tz_;
};
bool TimeZoneWrapper::load(const std::string &timezone)
{
return cctz::load_time_zone(timezone, &tz_);
}
void TimeZoneWrapper::load_utc_timezone()
{
tz_ = cctz::utc_time_zone();
}
void TimeZoneWrapper::load_local_timezone()
{
tz_ = cctz::local_time_zone();
}
void TimeZoneWrapper::load_fixed_timezone(int32_t offset)
{
tz_ = cctz::fixed_time_zone(std::chrono::duration<int32_t>(offset));
}
cctz::time_zone::civil_lookup TimeZoneWrapper::lookup(const cctz::civil_second& sec)
{
return tz_.lookup(sec);
}
cctz::time_zone::absolute_lookup TimeZoneWrapper::lookup(uint64_t seconds)
{
return tz_.lookup(timestamp2tp(seconds));
}
std::string TimeZoneWrapper::format(const std::string& format, std::chrono::system_clock::time_point tp)
{
return cctz::format(format, tp, tz_);
}
}
struct enif_timezone
{
TimeZoneWrapper* tz;
};
void enif_timezone_free(ErlNifEnv* env, void* obj)
{
UNUSED(env);
enif_timezone* timezone = static_cast<enif_timezone*>(obj);
if(timezone->tz)
delete timezone->tz;
}
ERL_NIF_TERM enif_timezone_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
elocatime_data* data = static_cast<elocatime_data*>(enif_priv_data(env));
scoped_ptr(timezone, enif_timezone, static_cast<enif_timezone*>(enif_alloc_resource(data->res_timezone, sizeof(enif_timezone))), enif_release_resource);
timezone->tz = new TimeZoneWrapper();
if(enif_is_binary(env, argv[0]))
{
std::string named_timezone;
if(!get_string(env, argv[0], &named_timezone))
return make_badarg(env);
if(!timezone->tz->load(named_timezone))
return make_error(env, kUnknownTimezone);
}
else if(enif_is_atom(env, argv[0]))
{
if(enif_is_identical(argv[0], ATOMS.atomTimezoneLocal))
timezone->tz->load_local_timezone();
else if(enif_is_identical(argv[0], ATOMS.atomTimezoneUtc))
timezone->tz->load_utc_timezone();
else
return make_error(env, kUnknownTimezone);
}
else if(enif_is_number(env, argv[0]))
{
int32_t offset;
if(!enif_get_int(env, argv[0], &offset))
return make_error(env, kUnknownTimezone);
timezone->tz->load_fixed_timezone(offset);
}
else
{
return make_badarg(env);
}
return make_ok_result(env, enif_make_resource(env, timezone.get()));
}
ERL_NIF_TERM enif_timezone_absolute_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
elocatime_data* data = static_cast<elocatime_data*>(enif_priv_data(env));
enif_timezone* timezone;
unsigned long timestamp;
if(!enif_get_resource(env, argv[1], data->res_timezone, (void**)&timezone))
return make_badarg(env);
if(!enif_get_uint64(env, argv[0], &timestamp))
return make_badarg(env);
cctz::time_zone::absolute_lookup lookup = timezone->tz->lookup(static_cast<uint64_t>(timestamp));
ERL_NIF_TERM nif_date = enif_make_tuple3(env,
enif_make_int(env, lookup.cs.year()),
enif_make_int(env, lookup.cs.month()),
enif_make_int(env, lookup.cs.day()));
ERL_NIF_TERM nif_time = enif_make_tuple3(env,
enif_make_int(env, lookup.cs.hour()),
enif_make_int(env, lookup.cs.minute()),
enif_make_int(env, lookup.cs.second()));
return make_ok_result(env, enif_make_tuple5(env,
ATOMS.atomAbsLookup,
enif_make_tuple2(env, nif_date, nif_time),
enif_make_int(env, lookup.offset),
lookup.is_dst ? ATOMS.atomTrue : ATOMS.atomFalse,
make_binary(env, lookup.abbr, strlen(lookup.abbr))));
}
ERL_NIF_TERM enif_timezone_civil_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
elocatime_data* data = static_cast<elocatime_data*>(enif_priv_data(env));
enif_timezone* timezone;
if(!enif_get_resource(env, argv[1], data->res_timezone, (void**)&timezone))
return make_badarg(env);
const ERL_NIF_TERM *datetime;
const ERL_NIF_TERM *date;
const ERL_NIF_TERM *time;
int arity;
if(!enif_get_tuple(env, argv[0], &arity, &datetime) || arity != 2)
return make_badarg(env);
if(!enif_get_tuple(env, datetime[0], &arity, &date) || arity != 3)
return make_badarg(env);
if(!enif_get_tuple(env, datetime[1], &arity, &time) || arity != 3)
return make_badarg(env);
int year, month, day, hour, minute, second;
if(!enif_get_int(env, date[0], &year) || !enif_get_int(env, date[1], &month) || !enif_get_int(env, date[2], &day))
return make_badarg(env);
if(!enif_get_int(env, time[0], &hour) || !enif_get_int(env, time[1], &minute) || !enif_get_int(env, time[2], &second))
return make_badarg(env);
cctz::civil_second civil_sec(year, month, day, hour, minute, second);
cctz::time_zone::civil_lookup lookup = timezone->tz->lookup(civil_sec);
return make_ok_result(env, enif_make_tuple5(env,
ATOMS.atomCivilLookup,
kind2atom(lookup.kind),
enif_make_uint64(env, tp2timestamp(lookup.pre)),
enif_make_uint64(env, tp2timestamp(lookup.trans)),
enif_make_uint64(env, tp2timestamp(lookup.post))));
}
ERL_NIF_TERM enif_format(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
UNUSED(argc);
elocatime_data* data = static_cast<elocatime_data*>(enif_priv_data(env));
std::string format;
unsigned long timestamp;
if(!get_string(env, argv[0], &format))
return make_badarg(env);
if(!enif_get_uint64(env, argv[1], &timestamp))
return make_badarg(env);
if(argc == 3)
{
enif_timezone* timezone;
if(!enif_get_resource(env, argv[2], data->res_timezone, (void**)&timezone))
return make_badarg(env);
std::string result = timezone->tz->format(format, timestamp2tp(timestamp));
return make_ok_result(env, make_binary(env, result.c_str(), result.size()));
}
std::string result = cctz::format(format, timestamp2tp(timestamp), kUtcTz);
return make_ok_result(env, make_binary(env, result.c_str(), result.size()));
}