tranfi 0.0.2 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +395 -0
- package/app/assets/index-6quYZ5Ap.css +5 -0
- package/app/assets/index-pDFMluyz.js +160 -0
- package/app/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/app/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/app/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/app/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/app/index.html +13 -0
- package/binding.gyp +69 -0
- package/csrc/arena.c +91 -0
- package/csrc/batch.c +229 -0
- package/csrc/buffer.c +78 -0
- package/csrc/cJSON.c +3143 -0
- package/csrc/cJSON.h +300 -0
- package/csrc/codec_csv.c +1058 -0
- package/csrc/codec_jsonl.c +374 -0
- package/csrc/codec_table.c +218 -0
- package/csrc/codec_text.c +229 -0
- package/csrc/compiler.c +102 -0
- package/csrc/date_utils.h +94 -0
- package/csrc/dsl.c +1180 -0
- package/csrc/dsl.h +22 -0
- package/csrc/expr.c +1245 -0
- package/csrc/expr.h +56 -0
- package/csrc/internal.h +250 -0
- package/csrc/ir.c +119 -0
- package/csrc/ir.h +167 -0
- package/csrc/ir_schema.c +60 -0
- package/csrc/ir_serialize.c +104 -0
- package/csrc/ir_sql.c +1211 -0
- package/csrc/ir_validate.c +120 -0
- package/csrc/main.c +392 -0
- package/csrc/op_acf.c +133 -0
- package/csrc/op_anomaly.c +120 -0
- package/csrc/op_bin.c +109 -0
- package/csrc/op_cast.c +195 -0
- package/csrc/op_clip.c +88 -0
- package/csrc/op_date_trunc.c +181 -0
- package/csrc/op_datetime.c +212 -0
- package/csrc/op_derive.c +248 -0
- package/csrc/op_diff.c +134 -0
- package/csrc/op_ewma.c +103 -0
- package/csrc/op_explode.c +108 -0
- package/csrc/op_fill_down.c +163 -0
- package/csrc/op_fill_null.c +123 -0
- package/csrc/op_filter.c +132 -0
- package/csrc/op_frequency.c +193 -0
- package/csrc/op_grep.c +163 -0
- package/csrc/op_group_agg.c +285 -0
- package/csrc/op_hash.c +126 -0
- package/csrc/op_head.c +149 -0
- package/csrc/op_interpolate.c +239 -0
- package/csrc/op_join.c +384 -0
- package/csrc/op_label_encode.c +144 -0
- package/csrc/op_lead.c +190 -0
- package/csrc/op_normalize.c +226 -0
- package/csrc/op_onehot.c +185 -0
- package/csrc/op_pivot.c +370 -0
- package/csrc/op_registry.c +1148 -0
- package/csrc/op_rename.c +138 -0
- package/csrc/op_replace.c +202 -0
- package/csrc/op_sample.c +101 -0
- package/csrc/op_select.c +140 -0
- package/csrc/op_skip.c +152 -0
- package/csrc/op_sort.c +273 -0
- package/csrc/op_split.c +114 -0
- package/csrc/op_split_data.c +87 -0
- package/csrc/op_stack.c +315 -0
- package/csrc/op_stats.c +779 -0
- package/csrc/op_step.c +171 -0
- package/csrc/op_tail.c +96 -0
- package/csrc/op_top.c +150 -0
- package/csrc/op_trim.c +109 -0
- package/csrc/op_unique.c +300 -0
- package/csrc/op_unpivot.c +159 -0
- package/csrc/op_validate.c +71 -0
- package/csrc/op_window.c +150 -0
- package/csrc/pipeline.c +315 -0
- package/csrc/plan.c +206 -0
- package/csrc/recipes.c +102 -0
- package/csrc/recipes.h +27 -0
- package/csrc/report.c +463 -0
- package/csrc/report.h +22 -0
- package/csrc/tranfi.h +123 -0
- package/csrc/wasm_api.c +157 -0
- package/napi_api.c +326 -0
- package/package.json +46 -57
- package/src/cli.js +193 -0
- package/src/engines/duckdb.js +109 -0
- package/src/index.js +306 -0
- package/src/native.js +22 -0
- package/src/pipeline.js +286 -0
- package/src/server.js +277 -0
- package/src/wasm.js +19 -0
- package/wasm/index.js +244 -0
- package/wasm/package.json +1 -0
- package/wasm/tranfi_core.js +0 -0
- package/LICENSE +0 -21
- package/dist/bundle.js +0 -1
- package/index.html +0 -18
- package/src/app.css +0 -169
- package/src/app.js +0 -203
- package/src/app.vue +0 -250
- package/src/bulma-input.vue +0 -110
- package/src/common-inputs.js +0 -28
- package/src/main.js +0 -20
- package/src/transforms.js +0 -166
- package/webpack.config.js +0 -108
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* codec_text.c — Plain text line codec (newline-split).
|
|
3
|
+
*
|
|
4
|
+
* Decoder: splits on newlines, each line → one row in single "_line" column.
|
|
5
|
+
* - No type detection, no field parsing — just memchr for newlines.
|
|
6
|
+
* - Emits batch at batch_size.
|
|
7
|
+
*
|
|
8
|
+
* Encoder: writes _line string + \n per row.
|
|
9
|
+
* - Fallback: if no _line column, concatenate all string columns with tab.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "internal.h"
|
|
13
|
+
#include "cJSON.h"
|
|
14
|
+
#include <stdlib.h>
|
|
15
|
+
#include <string.h>
|
|
16
|
+
|
|
17
|
+
#define DEFAULT_BATCH_SIZE 1024
|
|
18
|
+
|
|
19
|
+
/* ================================================================
|
|
20
|
+
* Text Decoder
|
|
21
|
+
* ================================================================ */
|
|
22
|
+
|
|
23
|
+
typedef struct {
|
|
24
|
+
size_t batch_size;
|
|
25
|
+
tf_buffer line_buf;
|
|
26
|
+
tf_batch *batch;
|
|
27
|
+
size_t rows_buffered;
|
|
28
|
+
} text_decoder_state;
|
|
29
|
+
|
|
30
|
+
static tf_batch *make_text_batch(size_t capacity) {
|
|
31
|
+
tf_batch *b = tf_batch_create(1, capacity);
|
|
32
|
+
if (!b) return NULL;
|
|
33
|
+
tf_batch_set_schema(b, 0, "_line", TF_TYPE_STRING);
|
|
34
|
+
return b;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static int text_decode(tf_decoder *self, const uint8_t *data, size_t len,
|
|
38
|
+
tf_batch ***out, size_t *n_out) {
|
|
39
|
+
text_decoder_state *st = self->state;
|
|
40
|
+
*out = NULL;
|
|
41
|
+
*n_out = 0;
|
|
42
|
+
|
|
43
|
+
if (tf_buffer_write(&st->line_buf, data, len) != TF_OK) return TF_ERROR;
|
|
44
|
+
|
|
45
|
+
size_t out_cap = 0;
|
|
46
|
+
uint8_t *buf = st->line_buf.data + st->line_buf.read_pos;
|
|
47
|
+
size_t buf_len = st->line_buf.len - st->line_buf.read_pos;
|
|
48
|
+
|
|
49
|
+
size_t line_start = 0;
|
|
50
|
+
for (size_t i = 0; i < buf_len; i++) {
|
|
51
|
+
if (buf[i] == '\n') {
|
|
52
|
+
size_t line_len = i - line_start;
|
|
53
|
+
/* Strip trailing \r for CRLF */
|
|
54
|
+
if (line_len > 0 && buf[line_start + line_len - 1] == '\r')
|
|
55
|
+
line_len--;
|
|
56
|
+
|
|
57
|
+
if (!st->batch) {
|
|
58
|
+
st->batch = make_text_batch(st->batch_size);
|
|
59
|
+
if (!st->batch) return TF_ERROR;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
size_t row = st->batch->n_rows;
|
|
63
|
+
if (tf_batch_ensure_capacity(st->batch, row + 1) != TF_OK)
|
|
64
|
+
return TF_ERROR;
|
|
65
|
+
|
|
66
|
+
/* Copy line into batch */
|
|
67
|
+
char *line_str = malloc(line_len + 1);
|
|
68
|
+
if (!line_str) return TF_ERROR;
|
|
69
|
+
memcpy(line_str, buf + line_start, line_len);
|
|
70
|
+
line_str[line_len] = '\0';
|
|
71
|
+
tf_batch_set_string(st->batch, row, 0, line_str);
|
|
72
|
+
free(line_str);
|
|
73
|
+
st->batch->n_rows = row + 1;
|
|
74
|
+
st->rows_buffered++;
|
|
75
|
+
|
|
76
|
+
line_start = i + 1;
|
|
77
|
+
|
|
78
|
+
/* Emit batch if full */
|
|
79
|
+
if (st->rows_buffered >= st->batch_size) {
|
|
80
|
+
if (*n_out >= out_cap) {
|
|
81
|
+
out_cap = (out_cap == 0) ? 4 : out_cap * 2;
|
|
82
|
+
*out = realloc(*out, out_cap * sizeof(tf_batch *));
|
|
83
|
+
}
|
|
84
|
+
(*out)[(*n_out)++] = st->batch;
|
|
85
|
+
st->batch = NULL;
|
|
86
|
+
st->rows_buffered = 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
st->line_buf.read_pos += line_start;
|
|
92
|
+
tf_buffer_compact(&st->line_buf);
|
|
93
|
+
return TF_OK;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static int text_flush(tf_decoder *self, tf_batch ***out, size_t *n_out) {
|
|
97
|
+
text_decoder_state *st = self->state;
|
|
98
|
+
*out = NULL;
|
|
99
|
+
*n_out = 0;
|
|
100
|
+
size_t out_cap = 0;
|
|
101
|
+
|
|
102
|
+
/* Process remaining data as a final line */
|
|
103
|
+
size_t remaining = tf_buffer_readable(&st->line_buf);
|
|
104
|
+
if (remaining > 0) {
|
|
105
|
+
uint8_t *buf = st->line_buf.data + st->line_buf.read_pos;
|
|
106
|
+
|
|
107
|
+
if (!st->batch) {
|
|
108
|
+
st->batch = make_text_batch(st->batch_size);
|
|
109
|
+
if (!st->batch) return TF_ERROR;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
size_t row = st->batch->n_rows;
|
|
113
|
+
if (tf_batch_ensure_capacity(st->batch, row + 1) != TF_OK)
|
|
114
|
+
return TF_ERROR;
|
|
115
|
+
|
|
116
|
+
/* Strip trailing \r */
|
|
117
|
+
size_t line_len = remaining;
|
|
118
|
+
if (line_len > 0 && buf[line_len - 1] == '\r')
|
|
119
|
+
line_len--;
|
|
120
|
+
|
|
121
|
+
char *line_str = malloc(line_len + 1);
|
|
122
|
+
if (!line_str) return TF_ERROR;
|
|
123
|
+
memcpy(line_str, buf, line_len);
|
|
124
|
+
line_str[line_len] = '\0';
|
|
125
|
+
tf_batch_set_string(st->batch, row, 0, line_str);
|
|
126
|
+
free(line_str);
|
|
127
|
+
st->batch->n_rows = row + 1;
|
|
128
|
+
st->rows_buffered++;
|
|
129
|
+
|
|
130
|
+
st->line_buf.read_pos = st->line_buf.len;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Emit remaining batch */
|
|
134
|
+
if (st->batch && st->rows_buffered > 0) {
|
|
135
|
+
if (*n_out >= out_cap) {
|
|
136
|
+
out_cap = (*n_out == 0) ? 1 : out_cap * 2;
|
|
137
|
+
*out = realloc(*out, out_cap * sizeof(tf_batch *));
|
|
138
|
+
}
|
|
139
|
+
(*out)[(*n_out)++] = st->batch;
|
|
140
|
+
st->batch = NULL;
|
|
141
|
+
st->rows_buffered = 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return TF_OK;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static void text_decoder_destroy(tf_decoder *self) {
|
|
148
|
+
text_decoder_state *st = self->state;
|
|
149
|
+
if (st) {
|
|
150
|
+
tf_buffer_free(&st->line_buf);
|
|
151
|
+
if (st->batch) tf_batch_free(st->batch);
|
|
152
|
+
free(st);
|
|
153
|
+
}
|
|
154
|
+
free(self);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
tf_decoder *tf_text_decoder_create(const cJSON *args) {
|
|
158
|
+
text_decoder_state *st = calloc(1, sizeof(text_decoder_state));
|
|
159
|
+
if (!st) return NULL;
|
|
160
|
+
|
|
161
|
+
st->batch_size = DEFAULT_BATCH_SIZE;
|
|
162
|
+
if (args) {
|
|
163
|
+
cJSON *bs = cJSON_GetObjectItemCaseSensitive(args, "batch_size");
|
|
164
|
+
if (cJSON_IsNumber(bs) && bs->valueint > 0)
|
|
165
|
+
st->batch_size = (size_t)bs->valueint;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
tf_buffer_init(&st->line_buf);
|
|
169
|
+
|
|
170
|
+
tf_decoder *dec = malloc(sizeof(tf_decoder));
|
|
171
|
+
if (!dec) { free(st); return NULL; }
|
|
172
|
+
dec->decode = text_decode;
|
|
173
|
+
dec->flush = text_flush;
|
|
174
|
+
dec->destroy = text_decoder_destroy;
|
|
175
|
+
dec->state = st;
|
|
176
|
+
return dec;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* ================================================================
|
|
180
|
+
* Text Encoder
|
|
181
|
+
* ================================================================ */
|
|
182
|
+
|
|
183
|
+
static int text_encode(tf_encoder *self, tf_batch *in, tf_buffer *out) {
|
|
184
|
+
(void)self;
|
|
185
|
+
|
|
186
|
+
/* Find _line column index */
|
|
187
|
+
int line_col = tf_batch_col_index(in, "_line");
|
|
188
|
+
|
|
189
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
190
|
+
if (line_col >= 0) {
|
|
191
|
+
/* Write _line column value */
|
|
192
|
+
if (!tf_batch_is_null(in, r, (size_t)line_col)) {
|
|
193
|
+
const char *s = tf_batch_get_string(in, r, (size_t)line_col);
|
|
194
|
+
tf_buffer_write(out, (const uint8_t *)s, strlen(s));
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
/* Fallback: concatenate all string columns with tab */
|
|
198
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
199
|
+
if (c > 0) tf_buffer_write(out, (const uint8_t *)"\t", 1);
|
|
200
|
+
if (!tf_batch_is_null(in, r, c) && in->col_types[c] == TF_TYPE_STRING) {
|
|
201
|
+
const char *s = tf_batch_get_string(in, r, c);
|
|
202
|
+
tf_buffer_write(out, (const uint8_t *)s, strlen(s));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
tf_buffer_write(out, (const uint8_t *)"\n", 1);
|
|
207
|
+
}
|
|
208
|
+
return TF_OK;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static int text_encoder_flush(tf_encoder *self, tf_buffer *out) {
|
|
212
|
+
(void)self; (void)out;
|
|
213
|
+
return TF_OK;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static void text_encoder_destroy(tf_encoder *self) {
|
|
217
|
+
free(self);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
tf_encoder *tf_text_encoder_create(const cJSON *args) {
|
|
221
|
+
(void)args;
|
|
222
|
+
tf_encoder *enc = malloc(sizeof(tf_encoder));
|
|
223
|
+
if (!enc) return NULL;
|
|
224
|
+
enc->encode = text_encode;
|
|
225
|
+
enc->flush = text_encoder_flush;
|
|
226
|
+
enc->destroy = text_encoder_destroy;
|
|
227
|
+
enc->state = NULL;
|
|
228
|
+
return enc;
|
|
229
|
+
}
|
package/csrc/compiler.c
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* compiler.c — L2 IR → L1 native target compilation.
|
|
3
|
+
*
|
|
4
|
+
* Iterates IR nodes, looks up each op in the registry,
|
|
5
|
+
* and calls create_native() to build live decoder/steps/encoder structs.
|
|
6
|
+
* Replaces the constructor dispatch loop that was in plan.c.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "ir.h"
|
|
10
|
+
#include "internal.h"
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
#include <stdio.h>
|
|
14
|
+
|
|
15
|
+
static void set_error(char **error, const char *msg) {
|
|
16
|
+
if (error) {
|
|
17
|
+
free(*error);
|
|
18
|
+
size_t len = strlen(msg) + 1;
|
|
19
|
+
*error = malloc(len);
|
|
20
|
+
if (*error) memcpy(*error, msg, len);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
int tf_compile_native(const tf_ir_plan *plan,
|
|
25
|
+
tf_decoder **out_decoder, tf_step ***out_steps,
|
|
26
|
+
size_t *out_n_steps, tf_encoder **out_encoder,
|
|
27
|
+
char **error) {
|
|
28
|
+
*out_decoder = NULL;
|
|
29
|
+
*out_steps = NULL;
|
|
30
|
+
*out_n_steps = 0;
|
|
31
|
+
*out_encoder = NULL;
|
|
32
|
+
if (error) *error = NULL;
|
|
33
|
+
|
|
34
|
+
/* Allocate steps array (max = n_nodes, since some are decoder/encoder) */
|
|
35
|
+
tf_step **steps = calloc(plan->n_nodes, sizeof(tf_step *));
|
|
36
|
+
if (!steps) {
|
|
37
|
+
set_error(error, "out of memory");
|
|
38
|
+
return TF_ERROR;
|
|
39
|
+
}
|
|
40
|
+
size_t n_steps = 0;
|
|
41
|
+
tf_decoder *decoder = NULL;
|
|
42
|
+
tf_encoder *encoder = NULL;
|
|
43
|
+
|
|
44
|
+
for (size_t i = 0; i < plan->n_nodes; i++) {
|
|
45
|
+
const tf_ir_node *node = &plan->nodes[i];
|
|
46
|
+
const tf_op_entry *entry = tf_op_registry_find(node->op);
|
|
47
|
+
|
|
48
|
+
if (!entry) {
|
|
49
|
+
char buf[256];
|
|
50
|
+
snprintf(buf, sizeof(buf), "unknown op: '%s'", node->op);
|
|
51
|
+
set_error(error, buf);
|
|
52
|
+
goto fail;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!entry->create_native) {
|
|
56
|
+
if (entry->tier == TF_TIER_ECOSYSTEM) {
|
|
57
|
+
/* Ecosystem stubs are skipped — host must handle */
|
|
58
|
+
char buf[256];
|
|
59
|
+
snprintf(buf, sizeof(buf), "op '%s' has no native target", node->op);
|
|
60
|
+
set_error(error, buf);
|
|
61
|
+
goto fail;
|
|
62
|
+
}
|
|
63
|
+
/* Core ops with NULL constructor are no-ops (e.g. flatten) */
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void *obj = entry->create_native(node->args);
|
|
68
|
+
if (!obj) {
|
|
69
|
+
char buf[256];
|
|
70
|
+
snprintf(buf, sizeof(buf), "failed to create '%s'", node->op);
|
|
71
|
+
set_error(error, buf);
|
|
72
|
+
goto fail;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
switch (entry->kind) {
|
|
76
|
+
case TF_OP_DECODER:
|
|
77
|
+
decoder = (tf_decoder *)obj;
|
|
78
|
+
break;
|
|
79
|
+
case TF_OP_ENCODER:
|
|
80
|
+
encoder = (tf_encoder *)obj;
|
|
81
|
+
break;
|
|
82
|
+
case TF_OP_TRANSFORM:
|
|
83
|
+
steps[n_steps++] = (tf_step *)obj;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
*out_decoder = decoder;
|
|
89
|
+
*out_steps = steps;
|
|
90
|
+
*out_n_steps = n_steps;
|
|
91
|
+
*out_encoder = encoder;
|
|
92
|
+
return TF_OK;
|
|
93
|
+
|
|
94
|
+
fail:
|
|
95
|
+
if (decoder) decoder->destroy(decoder);
|
|
96
|
+
if (encoder) encoder->destroy(encoder);
|
|
97
|
+
for (size_t i = 0; i < n_steps; i++) {
|
|
98
|
+
if (steps[i]) steps[i]->destroy(steps[i]);
|
|
99
|
+
}
|
|
100
|
+
free(steps);
|
|
101
|
+
return TF_ERROR;
|
|
102
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* date_utils.h — Date/timestamp conversion utilities (header-only).
|
|
3
|
+
*
|
|
4
|
+
* Uses Howard Hinnant's civil date algorithms (public domain).
|
|
5
|
+
* Epoch: 1970-01-01. Date = int32_t days. Timestamp = int64_t microseconds.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#ifndef TF_DATE_UTILS_H
|
|
9
|
+
#define TF_DATE_UTILS_H
|
|
10
|
+
|
|
11
|
+
#include <stdint.h>
|
|
12
|
+
#include <stdio.h>
|
|
13
|
+
|
|
14
|
+
/* Days from civil date to epoch (1970-01-01 = 0). */
|
|
15
|
+
static inline int32_t tf_date_from_ymd(int y, int m, int d) {
|
|
16
|
+
y -= (m <= 2);
|
|
17
|
+
int era = (y >= 0 ? y : y - 399) / 400;
|
|
18
|
+
unsigned yoe = (unsigned)(y - era * 400);
|
|
19
|
+
unsigned doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + (unsigned)d - 1;
|
|
20
|
+
unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
|
21
|
+
return era * 146097 + (int)doe - 719468;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Epoch days back to civil date. */
|
|
25
|
+
static inline void tf_date_to_ymd(int32_t days, int *y, int *m, int *d) {
|
|
26
|
+
days += 719468;
|
|
27
|
+
int era = (days >= 0 ? days : days - 146096) / 146097;
|
|
28
|
+
unsigned doe = (unsigned)(days - era * 146097);
|
|
29
|
+
unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
|
30
|
+
*y = (int)yoe + era * 400;
|
|
31
|
+
unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
32
|
+
unsigned mp = (5 * doy + 2) / 153;
|
|
33
|
+
*d = (int)(doy - (153 * mp + 2) / 5 + 1);
|
|
34
|
+
*m = (int)(mp < 10 ? mp + 3 : mp - 9);
|
|
35
|
+
*y += (*m <= 2);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Microseconds since epoch to date/time components. */
|
|
39
|
+
static inline void tf_timestamp_to_parts(int64_t us, int *y, int *mo, int *d,
|
|
40
|
+
int *h, int *mi, int *s, int *frac_us) {
|
|
41
|
+
int64_t day_us = 86400LL * 1000000LL;
|
|
42
|
+
int32_t days = (int32_t)(us / day_us);
|
|
43
|
+
int64_t rem = us % day_us;
|
|
44
|
+
if (rem < 0) { days--; rem += day_us; }
|
|
45
|
+
tf_date_to_ymd(days, y, mo, d);
|
|
46
|
+
*frac_us = (int)(rem % 1000000);
|
|
47
|
+
rem /= 1000000;
|
|
48
|
+
*s = (int)(rem % 60); rem /= 60;
|
|
49
|
+
*mi = (int)(rem % 60); rem /= 60;
|
|
50
|
+
*h = (int)rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Date/time components to microseconds since epoch. */
|
|
54
|
+
static inline int64_t tf_timestamp_from_parts(int y, int mo, int d,
|
|
55
|
+
int h, int mi, int s, int frac_us) {
|
|
56
|
+
int32_t days = tf_date_from_ymd(y, mo, d);
|
|
57
|
+
return (int64_t)days * 86400LL * 1000000LL
|
|
58
|
+
+ (int64_t)h * 3600000000LL
|
|
59
|
+
+ (int64_t)mi * 60000000LL
|
|
60
|
+
+ (int64_t)s * 1000000LL
|
|
61
|
+
+ frac_us;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Weekday: 0=Sunday..6=Saturday. */
|
|
65
|
+
static inline int tf_date_weekday(int32_t days) {
|
|
66
|
+
return days >= -4 ? (days + 4) % 7 : (days + 5) % 7 + 6;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Format date as YYYY-MM-DD into buf (must be >= 11 bytes). Returns buf. */
|
|
70
|
+
static inline char *tf_date_format(int32_t days, char *buf, size_t buflen) {
|
|
71
|
+
int y, m, d;
|
|
72
|
+
tf_date_to_ymd(days, &y, &m, &d);
|
|
73
|
+
snprintf(buf, buflen, "%04d-%02d-%02d", y, m, d);
|
|
74
|
+
return buf;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Format timestamp as ISO 8601 into buf (must be >= 32 bytes). Returns buf. */
|
|
78
|
+
static inline char *tf_timestamp_format(int64_t us, char *buf, size_t buflen) {
|
|
79
|
+
int y, mo, d, h, mi, s, frac;
|
|
80
|
+
tf_timestamp_to_parts(us, &y, &mo, &d, &h, &mi, &s, &frac);
|
|
81
|
+
if (frac > 0) {
|
|
82
|
+
int len = snprintf(buf, buflen, "%04d-%02d-%02dT%02d:%02d:%02d.%06d",
|
|
83
|
+
y, mo, d, h, mi, s, frac);
|
|
84
|
+
/* Trim trailing zeros */
|
|
85
|
+
while (len > 0 && buf[len - 1] == '0') len--;
|
|
86
|
+
buf[len++] = 'Z';
|
|
87
|
+
buf[len] = '\0';
|
|
88
|
+
} else {
|
|
89
|
+
snprintf(buf, buflen, "%04d-%02d-%02dT%02d:%02d:%02dZ", y, mo, d, h, mi, s);
|
|
90
|
+
}
|
|
91
|
+
return buf;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#endif /* TF_DATE_UTILS_H */
|