Files
shiftcraft/.claude/skills/pocketbase-best-practices/rules/rules-strftime.md
2026-04-17 23:26:01 +00:00

74 lines
2.8 KiB
Markdown

---
title: Use strftime() for Date Arithmetic in Filter Expressions
impact: MEDIUM
impactDescription: strftime() (added in v0.36) replaces brittle string prefix comparisons on datetime fields
tags: filter, strftime, datetime, rules, v0.36
---
## Use strftime() for Date Arithmetic in Filter Expressions
PocketBase v0.36 added the `strftime()` function to the filter expression grammar. It maps directly to SQLite's [strftime](https://sqlite.org/lang_datefunc.html) and is the correct way to bucket, compare, or extract parts of a datetime field. Before v0.36 people worked around this with `~` (substring) matches against the ISO string; those workarounds are fragile (they break at midnight UTC, ignore timezones, and can't handle ranges).
**Incorrect (substring match on the ISO datetime string):**
```javascript
// ❌ "matches anything whose ISO string contains 2026-04-08" - breaks as soon
// as your DB stores sub-second precision or you cross a month boundary
const todayPrefix = new Date().toISOString().slice(0, 10);
const results = await pb.collection("orders").getList(1, 50, {
filter: `created ~ "${todayPrefix}"`, // ❌
});
```
**Correct (strftime with named format specifiers):**
```javascript
// "all orders created today (UTC)"
const results = await pb.collection("orders").getList(1, 50, {
filter: `strftime('%Y-%m-%d', created) = strftime('%Y-%m-%d', @now)`,
});
// "all orders from March 2026"
await pb.collection("orders").getList(1, 50, {
filter: `strftime('%Y-%m', created) = "2026-03"`,
});
// "orders created this hour"
await pb.collection("orders").getList(1, 50, {
filter: `strftime('%Y-%m-%d %H', created) = strftime('%Y-%m-%d %H', @now)`,
});
```
```javascript
// Same function is available inside API rules:
// collection "orders" - List rule:
// @request.auth.id != "" &&
// user = @request.auth.id &&
// strftime('%Y-%m-%d', created) = strftime('%Y-%m-%d', @now)
```
**Common format specifiers:**
| Specifier | Meaning |
|---|---|
| `%Y` | 4-digit year |
| `%m` | month (01-12) |
| `%d` | day of month (01-31) |
| `%H` | hour (00-23) |
| `%M` | minute (00-59) |
| `%S` | second (00-59) |
| `%W` | ISO week (00-53) |
| `%j` | day of year (001-366) |
| `%w` | day of week (0=Sunday) |
**Other filter functions worth knowing:**
| Function | Use |
|---|---|
| `strftime(fmt, datetime)` | Format/extract datetime parts (v0.36+) |
| `length(field)` | Count elements in a multi-value field (file, relation, select) |
| `each(field, expr)` | Iterate over multi-value fields: `each(tags, ? ~ "urgent")` |
| `issetIf(field, val)` | Conditional presence check used in complex rules |
Reference: [Filter Syntax - Functions](https://pocketbase.io/docs/api-rules-and-filters/#filters) · [v0.36.0 release](https://github.com/pocketbase/pocketbase/releases/tag/v0.36.0)