Đọc lịch iCalendar API
Đọc lịch .ics thành sự kiện JSON, mở rộng lịch lặp RRULE trong khoảng ngày, đổi múi giờ, gộp nhiều lịch và tìm khung giờ trống.
Gửi nội dung iCalendar qua ics (hoặc body thô text/calendar), file, url / urls (feed công khai, nhận cả webcal://, tối đa 5 mỗi request) hoặc calendars (mảng nội dung hoặc đối tượng {url}) để gộp nhiều lịch. Mỗi sự kiện trả về tiêu đề, mô tả, địa điểm, URL, trạng thái, độ bận, nhóm, người tổ chức và người tham dự, nhắc nhở, start / end theo múi giờ đích timezone (ISO 8601 có độ lệch; YYYY-MM-DD cho sự kiện cả ngày, ngày kết thúc không tính), start_utc / end_utc, thời lượng và múi giờ gốc. Thời gian có TZID dùng múi giờ IANA, hoặc VTIMEZONE trong chính file lịch khi tên không phải IANA (Outlook), một số tên múi giờ Windows được quy đổi, thời gian “floating” được hiểu theo múi giờ đích.
Khi có from / to (ngày tính cả hai đầu, theo múi giờ đích), chuỗi sự kiện lặp được mở rộng: RRULE với FREQ (từ phút đến năm), INTERVAL, COUNT, UNTIL, BYDAY có thứ tự (1FR, -1SU), BYMONTHDAY, BYMONTH, BYYEARDAY, BYHOUR / BYMINUTE / BYSECOND, BYSETPOS và WKST, cùng RDATE, EXDATE và các lần bị dời hoặc huỷ (RECURRENCE-ID). Lịch lặp giữ nguyên giờ địa phương khi đổi giờ mùa hè. Không có khoảng ngày thì sự kiện được trả về như trong file kèm rrule (expand=true để liệt kê các lần đầu); limit giới hạn số kết quả (mặc định 1000, tối đa 5000).
free_slots=true gộp thời gian bận của mọi lịch (bỏ qua sự kiện đã huỷ và sự kiện “rảnh”, sự kiện cả ngày chỉ tính khi all_day_busy=true) và trả về các khoảng trống trong giờ làm việc (work_start, work_end, work_days theo thứ ISO, min_minutes) trong tối đa 62 ngày (mặc định 7 ngày từ hôm nay). Không lưu dữ liệu.
Tham số
| Tên | Vị trí | Kiểu | Bắt buộc | Mặc định | |
|---|---|---|---|---|---|
ics | body | string | không | Nội dung iCalendar (hoặc gửi body thô text/calendar). | |
file | body | file | không | File .ics (multipart). | |
url | body|query | string | không | URL feed .ics / webcal công khai; urls cho nhiều feed (tối đa 5). | |
calendars | body | array | không | Nhiều lịch cần gộp: nội dung hoặc đối tượng {url} / {ics} (tối đa 10). | |
timezone | body|query | string | không | Múi giờ IANA đích. Mặc định: X-WR-TIMEZONE của lịch, nếu không có thì UTC. | |
from | body|query | string | không | Đầu khoảng (YYYY-MM-DD hoặc ngày giờ ISO); bật mở rộng lịch lặp. | |
to | body|query | string | không | Cuối khoảng (ngày được tính cả ngày đó). Mặc định: 1 năm sau from. | |
expand | body|query | boolean | không | Mở rộng lịch lặp (mặc định bật khi có khoảng ngày). | |
limit | body|query | number | không | 1000 | Số sự kiện tối đa trả về (1–5000). |
include_cancelled | body|query | boolean | không | false | Giữ cả sự kiện có STATUS:CANCELLED. |
free_slots | body|query | boolean | không | false | Trả thêm các khoảng trống trong giờ làm việc. |
work_start | body|query | string | không | 09:00 | Giờ bắt đầu làm việc (HH:MM). |
work_end | body|query | string | không | 17:00 | Giờ kết thúc làm việc (HH:MM). |
work_days | body|query | string | không | 1-5 | Thứ theo ISO, 1 = thứ Hai … 7 = Chủ nhật, ví dụ 1-5 hoặc 1-6. |
min_minutes | body|query | number | không | 30 | Khoảng trống ngắn nhất được trả về (5–1440 phút). |
all_day_busy | body|query | boolean | không | false | Tính sự kiện cả ngày là bận. |
Ví dụ
curl -X POST "https://apibox.io.vn/v1/ics-parse" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ics":"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//APIBox//demo//VI\r\nX-WR-CALNAME:Nhóm dự án\r\nX-WR-TIMEZONE:Asia/Ho_Chi_Minh\r\nBEGIN:VEVENT\r\nUID:standup@example.vn\r\nDTSTART;TZID=Asia/Ho_Chi_Minh:20261116T090000\r\nDTEND;TZID=Asia/Ho_Chi_Minh:20261116T093000\r\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=12\r\nSUMMARY:Họp đầu ngày\r\nLOCATION:Phòng 3\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:demo@example.vn\r\nDTSTART:20261117T070000Z\r\nDTEND:20261117T083000Z\r\nSUMMARY:Demo khách hàng\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n","from":"2026-11-16","to":"2026-11-17","free_slots":true,"work_start":"08:30","work_end":"17:30"}'const res = await fetch('https://apibox.io.vn/v1/ics-parse', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"ics": "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//APIBox//demo//VI\r\nX-WR-CALNAME:Nhóm dự án\r\nX-WR-TIMEZONE:Asia/Ho_Chi_Minh\r\nBEGIN:VEVENT\r\nUID:standup@example.vn\r\nDTSTART;TZID=Asia/Ho_Chi_Minh:20261116T090000\r\nDTEND;TZID=Asia/Ho_Chi_Minh:20261116T093000\r\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=12\r\nSUMMARY:Họp đầu ngày\r\nLOCATION:Phòng 3\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:demo@example.vn\r\nDTSTART:20261117T070000Z\r\nDTEND:20261117T083000Z\r\nSUMMARY:Demo khách hàng\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
"from": "2026-11-16",
"to": "2026-11-17",
"free_slots": true,
"work_start": "08:30",
"work_end": "17:30"
}),
});
const data = await res.json();
console.log(data);import requests
res = requests.post(
"https://apibox.io.vn/v1/ics-parse",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"ics": "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//APIBox//demo//VI\r\nX-WR-CALNAME:Nhóm dự án\r\nX-WR-TIMEZONE:Asia/Ho_Chi_Minh\r\nBEGIN:VEVENT\r\nUID:standup@example.vn\r\nDTSTART;TZID=Asia/Ho_Chi_Minh:20261116T090000\r\nDTEND;TZID=Asia/Ho_Chi_Minh:20261116T093000\r\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=12\r\nSUMMARY:Họp đầu ngày\r\nLOCATION:Phòng 3\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:demo@example.vn\r\nDTSTART:20261117T070000Z\r\nDTEND:20261117T083000Z\r\nSUMMARY:Demo khách hàng\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", "from": "2026-11-16", "to": "2026-11-17", "free_slots": True, "work_start": "08:30", "work_end": "17:30"},
timeout=30,
)
print(res.json())<?php
$ch = curl_init('https://apibox.io.vn/v1/ics-parse');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_KEY', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => '{"ics":"BEGIN:VCALENDAR\\r\\nVERSION:2.0\\r\\nPRODID:-//APIBox//demo//VI\\r\\nX-WR-CALNAME:Nhóm dự án\\r\\nX-WR-TIMEZONE:Asia/Ho_Chi_Minh\\r\\nBEGIN:VEVENT\\r\\nUID:standup@example.vn\\r\\nDTSTART;TZID=Asia/Ho_Chi_Minh:20261116T090000\\r\\nDTEND;TZID=Asia/Ho_Chi_Minh:20261116T093000\\r\\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=12\\r\\nSUMMARY:Họp đầu ngày\\r\\nLOCATION:Phòng 3\\r\\nEND:VEVENT\\r\\nBEGIN:VEVENT\\r\\nUID:demo@example.vn\\r\\nDTSTART:20261117T070000Z\\r\\nDTEND:20261117T083000Z\\r\\nSUMMARY:Demo khách hàng\\r\\nEND:VEVENT\\r\\nEND:VCALENDAR\\r\\n","from":"2026-11-16","to":"2026-11-17","free_slots":true,"work_start":"08:30","work_end":"17:30"}',
]);
$response = curl_exec($ch);
print_r(json_decode($response, true));Kết quả trả về
{
"calendars": [
{
"index": 0,
"source": "ics",
"name": "Nhóm dự án",
"timezone": "Asia/Ho_Chi_Minh",
"prodid": "-//APIBox//demo//VI",
"method": null,
"events": 2
}
],
"timezone": "Asia/Ho_Chi_Minh",
"range": {
"from": "2026-11-16T00:00:00+07:00",
"to": "2026-11-18T00:00:00+07:00"
},
"expanded": true,
"count": 2,
"truncated": false,
"events": [
{
"uid": "standup@example.vn",
"summary": "Họp đầu ngày",
"start": "2026-11-16T09:00:00+07:00",
"end": "2026-11-16T09:30:00+07:00",
"start_utc": "2026-11-16T02:00:00.000Z",
"end_utc": "2026-11-16T02:30:00.000Z",
"all_day": false,
"duration_minutes": 30,
"timezone": "Asia/Ho_Chi_Minh",
"location": "Phòng 3",
"recurring": true
},
{
"uid": "demo@example.vn",
"summary": "Demo khách hàng",
"start": "2026-11-17T14:00:00+07:00",
"end": "2026-11-17T15:30:00+07:00",
"start_utc": "2026-11-17T07:00:00.000Z",
"end_utc": "2026-11-17T08:30:00.000Z",
"all_day": false,
"duration_minutes": 90,
"timezone": "UTC"
}
],
"free_slots": {
"work_hours": {
"start": "08:30",
"end": "17:30",
"days": [
1,
2,
3,
4,
5
]
},
"min_minutes": 30,
"busy": [
{
"start": "2026-11-16T09:00:00+07:00",
"end": "2026-11-16T09:30:00+07:00"
},
{
"start": "2026-11-17T14:00:00+07:00",
"end": "2026-11-17T15:30:00+07:00"
}
],
"slots": [
{
"start": "2026-11-16T08:30:00+07:00",
"end": "2026-11-16T09:00:00+07:00",
"minutes": 30
},
{
"start": "2026-11-16T09:30:00+07:00",
"end": "2026-11-16T17:30:00+07:00",
"minutes": 480
},
{
"start": "2026-11-17T08:30:00+07:00",
"end": "2026-11-17T14:00:00+07:00",
"minutes": 330
},
{
"start": "2026-11-17T15:30:00+07:00",
"end": "2026-11-17T17:30:00+07:00",
"minutes": 120
}
],
"total_free_minutes": 960
}
}
Ứng dụng
- Hiển thị feed lịch Google, Outlook, iCloud lên website dưới dạng JSON
- Liệt kê mọi buổi họp, buổi học lặp lại trong tháng
- Tìm giờ trống chung trên lịch của nhiều người
- Nhập file .ics vào hệ thống đặt lịch hoặc nhân sự của bạn
Hỏi đáp
Vì sao một số sự kiện lặp có vẻ lệch một giờ?
Không lệch: mỗi lần lặp giữ giờ địa phương theo múi giờ của sự kiện, nên giờ UTC thay đổi khi bắt đầu hay kết thúc giờ mùa hè, giống các ứng dụng lịch.
Phần nào của RRULE chưa hỗ trợ?
BYWEEKNO và FREQ=SECONDLY; các chuỗi này chỉ trả về lần đầu tiên kèm cảnh báo.
Lịch của tôi có bị lưu lại không?
Không. Lịch được đọc trong bộ nhớ cho từng request rồi bỏ đi; feed URL được tải lại mỗi lần.
API liên quan
Kiểm tra JSON
Kiểm tra cú pháp JSON, báo đúng dòng và cột bị lỗi, và validate dữ liệu theo JSON Schema (draft 4, 7, 2019-09 hoặc 2020-12).
{ }Định dạng JSON
Làm đẹp JSON với thụt lề 0-8 dấu cách hoặc tab, rút gọn (minify) hoặc sắp xếp key theo bảng chữ cái. Báo lỗi cú pháp kèm dòng và cột.
CSVCSV sang JSON
Chuyển CSV thành mảng object JSON theo dòng tiêu đề, tự nhận dấu phân cách, tự chuyển số và boolean, báo lỗi từng dòng.