মোবাইল API-এ JSON বনাম Protobuf: পে-লোড আকার, সামঞ্জস্য ও ডিবাগিং
মোবাইল API-এ JSON vs Protobuf: পে-লোড সাইজ, সামঞ্জস্য এবং ডিবাগিং ট্রেডঅফ ব্যাখ্যা—কখন টেক্সট বা বাইনারি বেছে নেবেন সে সম্পর্কে ব্যবহারিক নিয়ম।
PostgreSQL-এ পুনরাবৃত্ত শিডিউল এবং টাইমজোনগুলো কীভাবে সঠিকভাবে সংরক্ষণ ও জেনারেট করবেন—স্টোরেজ ফরম্যাট, রিকারেন্স নিয়ম, ব্যতিক্রম এবং কুয়েরি প্যাটার্নের ব্যবহার যা ক্যালেন্ডারকে সঠিক রাখে।

timestamptz ব্যবহার করুন: বুকিং, ক্লক-ইন, নোটিফিকেশন এবং এমন কিছু যেটা ব্যবহারকারী বা অঞ্চলের উপর ক্রস-তুলনা করা হয়। PostgreSQL এটি একটি অ্যাবসলুট ইনস্ট্যান্ট হিসেবে সংরক্ষণ করে এবং প্রদর্শনের জন্য রূপান্তর করে, ফলে অর্ডার এবং ওভারল্যাপ চেক ঠিকভাবে কাজ করে।\n\nলোকাল ওয়াল-ক্লক মানগুলোর জন্য timestamp without time zone ব্যবহার করুন, যেমন "প্রতিটি সোমবার 09:00" বা "দোকান খোলে 10:00"। এটি একটি টাইম জোন আইডির সাথে জোড়ান, এবং occurrences তৈরি করার সময়ই বাস্তব ইনস্ট্যান্টে রূপান্তর করবেন।\n\nপুনরাবৃত্ত প্যাটার্নের জন্য মৌলিক টাইপগুলো সহায়তা করে:\n\n- date দিনের-শুধু ব্যতিক্রম (ছুটির দিন) জন্য\n- time দৈনন্দিন শুরুর সময়ের জন্য\n- interval মেয়াদ (যেমন 6 ঘন্টার শিফট) জন্য\n\nটাইমজোনটি IANA নামে (উদাহরণ: America/New_York) text কলামে (বা ছোট লুকআপ টেবিলে) সংরক্ষণ করুন। -0500 মতো offsets যথেষ্ট নয় কারণ সেগুলোতে DST নিয়ম থাকে না।\n\nঅনেক অ্যাপের জন্য একটি ব্যবহারিক সেট:\n\n- বুক করা অ্যাপয়েন্টমেন্টের start/end ইনস্ট্যান্টের জন্য timestamptz\n- ব্যতিক্রম দিনের জন্য date\n- পুনরাবৃত্ত লোকাল শুরুর সময়ের জন্য time\n- ডিউরেশনের জন্য interval\n- IANA টাইম জোন ID জন্য text\n\n## বুকিং ও শিফট অ্যাপের জন্য ডেটা মডেল অপশনসমূহ\n\nসেরা স্কিমা নির্ভর করে শিডিউল কত ঘন ঘন বদলায় এবং মানুষ কত দূর অগ্রিম ব্রাউজ করে। সাধারণত আপনি অনেক রো আগাম লিখবেন না কি রিড-টাইমে জেনারেট করবেন তা বেছে নিচ্ছেন।\n\n### অপশন A: প্রতিটি occurrence সংরক্ষণ করুন\n\nপ্রতি শিফট বা বুকিংয়ের জন্য (এক্সপ্যান্ড করা) একটি রো ইনসার্ট করুন। কুয়েরি করা সহজ এবং বোধগম্য। তদ্ব্যতীত অনেক লেখনী আর নিয়ম বদলে গেলে অনেক আপডেট করতে হবে।\n\nএটি ভাল যখন ইভেন্টগুলো অধিকাংশই একক, অথবা যখন আপনি শুধুমাত্র সংক্ষিপ্ত আগাম (যেমন পরবর্তী ৩০ দিন) occurrence তৈরি করেন।\n\n### অপশন B: একটি রুল সংরক্ষণ করে রিড-টাইমে এক্সপ্যান্ড করুন\n\nএকটি শিডিউল রুল সংরক্ষণ করুন (যেমন "সপ্তাহে সোমবার ও বুধবার 09:00 America/New_York") এবং অন-ডিমান্ড অনুরোধকৃত রেঞ্জের জন্য occurrences জেনারেট করুন।\n\nএটি নমনীয় এবং স্টোরেজ হালকা, কিন্তু কুয়েরি জটিল হয়ে যায়। মাস ভিউ ধীর হতে পারে যদি আপনি ক্যাশিং না করেন।\n\n### অপশন C: রুল প্লাস ক্যাশড occurrences (হাইব্রিড)\n\nরুলকে সোর্স অফ ট্রুথ হিসেবে রাখুন, এবং একই সাথে একটি রোলিং উইন্ডোর জন্য তৈরি করা occurrences সংরক্ষণ করুন (উদাহরণ: 60-90 দিন)। রুল বদলালে ক্যাশ রিজেনারেট করুন।\n\nএটি শিফট অ্যাপের জন্য শক্তিশালী ডিফল্ট: মাস ভিউ দ্রুত থাকে, কিন্তু প্যাটার্ন সম্পাদনা করার এক জায়গা থাকে।\n\nপ্র্যাকটিক্যাল টেবিল সেট:\n\n- schedule: owner/resource, time zone, local start time, duration, recurrence rule\n- occurrence: বিস্তৃত ইনস্ট্যান্সগুলো start_at timestamptz, end_at timestamptz, এবং স্ট্যাটাসসহ\n- exception: "এই তারিখটি বাদ দিন" বা "এই তারিখটি আলাদা" চিন্হগুলো\n- override: প্রতি-occurrence এডিট যেমন পরিবর্তিত শুরুর সময়, বদলে যাওয়া স্টাফ, বাতিল ফ্ল্যাগ\n- (ঐচ্ছিক) schedule_cache_state: শেষ জেনারেট করা রেঞ্জ যাতে আপনি জানেন পরেরবার কি পূরণ করতে হবে\n\nক্যালেন্ডার রেঞ্জ কুয়েরিসের জন্য, "এই উইন্ডোতে সব দেখাও" ধাঁচে ইনডেক্স করুন:\n\n- occurrence তে: btree (resource_id, start_at) এবং প্রায়ই btree (resource_id, end_at)\n- যদি আপনি প্রায়ই "overlaps range" কুয়েরি করেন: একটি সৃষ্ট tstzrange(start_at, end_at) এবং gist ইনডেক্স\n\n## পুনরাবৃত্তি নিয়মগুলো কিভাবে দুর্বল করা যাবে না এমনভাবে উপস্থাপন করবেন\n\nরুলগুলো ভাঙ্গে যখন তা খুব চতুর, খুব নমনীয় বা অন অনুসন্ধানযোগ্য ব্লব হিসেবে সংরক্ষিত থাকে। একটি ভাল রুল ফরম্যাট এমন হওয়া উচিত যেটা আপনার অ্যাপ যাচাই করতে পারে এবং আপনার টিম দ্রুত ব্যাখ্যা করতে পারে।\n\nদুইটি সাধারণ পদ্ধতি:\n\n- আপনি যেসব প্যাটার্ন বাস্তবে সাপোর্ট করেন সেগুলোর জন্য সরল কাস্টম ফিল্ড।\n- অনেক কম্বিনেশন ইমপোর্ট/এক্সপোর্ট করতে হলে iCalendar-সদৃশ RRULE।\n\nএকটি ব্যবহারিক মধ্য্মপথ: সীমিত সেটের অপশন অনুমোদন করে সেগুলো কলামে সংরক্ষণ করুন, এবং যেকোন RRULE স্ট্রিংকে শুধু বিনিময়ের উদ্দেশ্যে রাখুন।\n\nউদাহরণস্বরূপ, একটি সাপ্তাহিক শিফট রুল নিচের ফিল্ডগুলোতে প্রকাশ করা যেতে পারে:\n\n- freq (daily/weekly/monthly) এবং interval (প্রতি N)\n- byweekday (0-6 অ্যারের মত অথবা বিটমাস্ক)\n- মাসিক নিয়মের জন্য ঐচ্ছিক bymonthday (1-31)\n- starts_at_local (যে লোকাল তারিখ+সময় ব্যবহারকারী বেছে নিয়েছে) এবং tzid\n- ঐচ্ছিক until_date অথবা count (উভয়ই সমর্থন করবেন না যদি প্রয়োজন না থাকে)\n\nসীমার জন্য, duration (যেমন 8 ঘন্টা) সংরক্ষণ করা ভাল, প্রতিটি occurrence-এর জন্য আলাদা end timestamp রাখার বদলে। ক্লক সরে গেলে duration স্থিতিশীল থাকে। আপনি প্রতিটি occurrence-এর জন্য end time হিসাব করতে পারেন: occurrence start + duration।\n\nরুল এক্সপ্যান্ড করার সময় নিরাপদ ও সীমিত রাখুন:\n\n- শুধুমাত্র window_start এবং window_end এর মধ্যে এক্সপ্যান্ড করুন।\n- ওভারনাইট ইভেন্টের জন্য একটি ছোট বাফার যোগ করুন (উদাহরণ: 1 দিন)।\n- সর্বোচ্চ ইন্সট্যান্সের পরে থামুন (যেমন 500)।\n- জেনারেট করার আগে প্রথমে প্রার্থী ফিল্টার করুন (tzid, freq, এবং start date দ্বারা)।\n\n## ধাপে ধাপে: DST-নিরাপদ পুনরাবৃত্ত শিডিউল তৈরি করা\n\nএকটি নির্ভরযোগ্য প্যাটার্ন হল: প্রতিটি occurrence-কে প্রথমে একটি লোকাল ক্যালেন্ডার ধারণা হিসেবে বিবেচনা করুন (তারিখ + লোকাল সময় + লোকেশনের টাইমজোন), তারপর কেবল যখন আপনাকে সাজানো, কনফ্লিক্ট চেক বা প্রদর্শন করতে হবে তখনই ইনস্ট্যান্টে রূপান্তর করুন।\n\n### 1) UTC অনুমান নয়, লোকাল ইচ্ছা সংরক্ষণ করুন\n\nশিডিউলের লোকেশন টাইমজোন (IANA নাম যেমন America/New_York) এবং একটি লোকাল শুরুর সময় (উদাহরণ 09:00) সংরক্ষণ করুন। এই লোকাল সময় ব্যবসার উদ্দেশ্যই, এমনকি যখন DST বদলে যায়।\n\nএকটি ডিউরেশন এবং রুলের স্পষ্ট সীমাও সংরক্ষণ করুন: একটি start date, এবং অথবা একটি end date বা repeat count। সীমারেখা "অনন্ত এক্সপ্যানশন" বাগ প্রতিরোধ করে।\n\n### 2) ব্যতিক্রম এবং ওভাররাইড আলাদাভাবে মডেল করুন\n\nদুটো ছোট টেবিল ব্যবহার করুন: একটি স্কিপ-তারিখের জন্য, আরেকটি পরিবর্তিত occurrence-র জন্য। সেগুলোকে schedule_id + local_date দ্বারা কীগুলো করে রাখুন যাতে মূল recurrence পরিষ্কারভাবে ম্যাচ করা যায়।\n\nএকটি ব্যবহারিক কাঠামো দেখতে এরকম:\n\nsql\n-- core schedule\n-- tz is the location time zone\n-- start_time is local wall-clock time\nschedule(id, tz text, start_date date, end_date date, start_time time, duration_mins int, by_dow int[])\n\nschedule_skip(schedule_id, local_date date)\n\nschedule_override(schedule_id, local_date date, new_start_time time, new_duration_mins int)\n\n\n### 3) শুধুমাত্র অনুরোধকৃত উইন্ডোর মধ্যে এক্সপ্যান্ড করুন\n\nযে রেঞ্জটি আপনি রেন্ডার করছেন (সপ্তাহ, মাস) সেই রেঞ্জের জন্য প্রার্থী লোকাল তারিখগুলো জেনারেট করুন, তারপর দিন-অফ-উইক দ্বারা ফিল্টার করুন, এবং পরে স্কিপ ও ওভাররাইড প্রয়োগ করুন।\n\nsql\nWITH days AS (\n SELECT d::date AS local_date\n FROM generate_series($1::date, $2::date, interval '1 day') d\n), base AS (\n SELECT s.id, s.tz, days.local_date,\n make_timestamp(extract(year from days.local_date)::int,\n extract(month from days.local_date)::int,\n extract(day from days.local_date)::int,\n extract(hour from s.start_time)::int,\n extract(minute from s.start_time)::int, 0) AS local_start\n FROM schedule s\n JOIN days ON days.local_date BETWEEN s.start_date AND s.end_date\n WHERE extract(dow from days.local_date)::int = ANY (s.by_dow)\n)\nSELECT b.id,\n (b.local_start AT TIME ZONE b.tz) AS start_utc\nFROM base b\nLEFT JOIN schedule_skip sk\n ON sk.schedule_id = b.id AND sk.local_date = b.local_date\nWHERE sk.schedule_id IS NULL;\n\n\n### 4) শেষে ভিউয়ারের জন্য রূপান্তর করুন\n\nstart_utc কে timestamptz হিসেবে রাখুন সাজানোর, কনফ্লিক্ট চেক এবং বুকিংয়ের জন্য। কেবল প্রদর্শনের সময় ভিউয়ারের টাইমজোনে রূপান্তর করুন। এতে DST-সম্পর্কিত অপ্রত্যাশিত ঘটনা এড়ানো যায় এবং ক্যালেন্ডার ভিউ স্থির থাকে।\n\n## সঠিক ক্যালেন্ডার ভিউ জেনারেট করার কুয়েরি প্যাটার্নসমূহ\n\nএকটি ক্যালেন্ডার স্ক্রিন সাধারণত একটি রেঞ্জ কুয়েরি: "আমাকে সব দেখাও from_ts এবং to_ts এর মধ্যে"। একটি নিরাপদ ধারা:\n\n1) শুধুমাত্র ঐ উইন্ডোর ভেতরের প্রার্থী এক্সপ্যান্ড করুন।\n2) ব্যতিক্রম/ওভাররাইড প্রয়োগ করুন।\n3) ফাইনাল রো আউটপুট করুন start_at এবং end_at হিসেবে timestamptz।\n\n### দৈনিক বা সাপ্তাহিক এক্সপানশন generate_series দিয়ে\n\nসহজ সাপ্তাহিক রুলগুলোর জন্য (যেমন "প্রতিটি সোম-শুক্র 09:00 লোকাল"), প্রথমে লোকাল তারিখগুলো জেনারেট করুন শিডিউলের টাইমজোনে, তারপর প্রতিটি লোকাল তারিখ + লোকাল টাইমকে একটি ইনস্ট্যান্টে রূপান্তর করুন।\n\nsql\n-- Inputs: :from_ts, :to_ts are timestamptz\n-- rule.tz is an IANA zone like 'America/New_York'\nWITH bounds AS (\n SELECT\n (:from_ts AT TIME ZONE rule.tz)::date AS from_local_date,\n (:to_ts AT TIME ZONE rule.tz)::date AS to_local_date\n FROM rule\n WHERE rule.id = :rule_id\n), days AS (\n SELECT d::date AS local_date\n FROM bounds, generate_series(from_local_date, to_local_date, interval '1 day') AS g(d)\n)\nSELECT\n (local_date + rule.start_local_time) AT TIME ZONE rule.tz AS start_at,\n (local_date + rule.end_local_time) AT TIME ZONE rule.tz AS end_at\nFROM rule\nJOIN days ON true\nWHERE EXTRACT(ISODOW FROM local_date) = ANY(rule.by_isodow);\n\n\nএটি ভালভাবে কাজ করে কারণ প্রতিটি occurrence-এ timestamptz-এ রূপান্তর হয়, তাই DST বদল সঠিক দিন অনুযায়ী প্রয়োগ করা হয়।\n\n### "nth weekday" বা কাস্টম ইন্টারভাল হলে রিকার্সিভ CTE\n\nযখন রুলগুলো "nth weekday"-এর ওপর নির্ভর করে, ফাঁক থাকে, বা কাস্টম ইন্টারভাল থাকে, তখন একটি রিকার্সিভ CTE প্রতিবার পরের occurrence জেনারেট করতে পারে যতক্ষণ না এটি to_ts পার করে। রিকার্সনকে উইন্ডোর সঙ্গে অ্যাঙ্কর করে রাখুন যাতে এটি অনন্ত চালিত না হয়।\n\nপ্রার্থী রো পাওয়া গেলে, exception এবং cancellation প্রয়োগ করুন (rule_id, start_at) বা স্থানীয় কী (rule_id, local_date) দ্বারা জোয়াইনে। যদি ক্যানসেল রেকর্ড থাকে, রো বাদ দিন। ওভাররাইড থাকলে start_at/end_at কে ওভাররাইড মান দিয়ে প্রতিস্থাপন করুন।\n\nকার্যক্ষমতা বিষয়ক প্যাটার্ন যা সবচেয়ে বেশি গুরুত্বপূর্ণ:\n\n- প্রথমেই রেঞ্জ সীমাবদ্ধ করুন: আগে রুলগুলো ফিল্টার করুন, তারপর কেবল [from_ts, to_ts)-এর মধ্যে এক্সপ্যান্ড করুন।\n- exception/override টেবিলগুলোকে (rule_id, start_at) বা (rule_id, local_date)-এ ইনডেক্স করুন।\n- মাস ভিউয়ের জন্য বছরের ডাটা এক্সপ্যান্ড করা থেকে বিরত থাকুন।\n- রুল বদলে গেলে ক্যাশ নিষ্ক্রিয় করতে পারলে কেবল তখনই expanded occurrences ক্যাশ করুন।\n\n## ব্যতিক্রম এবং ওভাররাইড পরিষ্কারভাবে হ্যান্ডল করা\n\nপুনরাবৃত্ত শিডিউলগুলা তখনই ব্যবহারযোগ্য যখন আপনি তা সহজে ভাঙতে পারেন। বুকিং ও শিফট অ্যাপে, "সাধারণ" সপ্তাহ হলো বেস রুল, এবং বাকী সবকিছু ব্যতিক্রম: ছুটি, বাতিল, সরানো অ্যাপয়েন্টমেন্ট বা স্টাফ বদল। ব্যতিক্রম পরে লাগালে ক্যালেন্ডার ভিউ ড্রিফট করে এবং ডুপলিকেট তৈরি হয়।\n\nতিনটি ধারণা আলাদা রাখুন:\n\n- একটি বেস স্কিডিউল (পুনরাবৃত্ত রুল এবং তার টাইমজোন)\n- স্কিপ (যে তারিখ বা ইনস্ট্যান্সগুলো ঘটবে না)\n- ওভাররাইড (একটি occurrence আছে, কিন্তু বিবরণ বদলে গেছে)\n\n### একটি স্থির প্রাধান্য নিয়ম ব্যবহার করুন\n\nএকটি ক্রম নির্বাচন করুন এবং তা কনসিস্টেন্ট রাখুন। একটি সাধারণ পছন্দ:\n\n1) বেস recurrence থেকে প্রার্থীরা জেনারেট করুন।\n2) ওভাররাইড প্রয়োগ করুন (জেনারেট হওয়া রো প্রতিস্থাপন করুন)।\n3) স্কিপ প্রয়োগ করুন (গোপন করুন)।\n\nনিয়মটি এক বাক্যে ব্যবহারকারীদের কাছে সহজে বোঝানো সম্ভব কিনা নিশ্চিত করুন।\n\n### যখন ওভাররাইড একটি ইনস্ট্যান্স প্রতিস্থাপন করে তখন ডুপ্লিকেট এড়ান\n\nডুপ্লিকেট সাধারণত তখন হয় যখন কুয়েরি উভয়: জেনারেটেড occurrence এবং ওভাররাইড রো ফিরিয়ে দেয়। এটা প্রতিরোধ করুন একটি স্থিতিশীল কী দিয়ে:\n\n- প্রতিটি জেনারেটেড ইনস্ট্যান্সকে একটি স্থিতিশীল কী দিন, যেমন (schedule_id, local_date, start_time, tzid)।\n- ওভাররাইড রোতে সেটি "original occurrence key" হিসেবে স্টোর করুন।\n- একটি unique constraint যোগ করুন যাতে প্রতিটি বেস occurrence-র জন্য শুধু একটি ওভাররাইড থাকতে পারে।\n\nতারপর কুয়েরিতে, মিল আছে এমন জেনারেটেড occurrence বাদ দিন এবং ওভাররাইড রো ইউনিয়ন করে যুক্ত করুন।\n\n### অডিটযোগ্যতা যোগ করুন কিন্তু ঝামেলা বাড়াবেন না\n\nব্যতিক্রমগুলোই বিতর্কের জায়গা ("কে আমার শিফট বদলালো?")। স্কিপ ও ওভাররাইডে বেসিক অডিট ফিল্ড যোগ করুন: created_by, created_at, updated_by, updated_at, এবং একটি ঐচ্ছিক কারণ।\n\n## এক-ঘন্টার দূরতত্রুটি ঘটানোর সাধারণ ভুলগুলো\n\nঅধিকাংশ এক-ঘন্টার বাগ আসে দুই ধরনের সময়ের মানে মিশে যাওয়ার ফলে: একটি ইনস্ট্যান্ট (UTC টাইমলাইনে একটি বিন্দু) এবং একটি লোকাল ক্লক রিডিং (যেমন নিউইর্কে প্রতিটি সোমবার 09:00)।\n\nএকটি ক্লাসিক ভুল হল একটি লোকাল ওয়াল-ক্লক রুল timestamptz হিসেবে সংরক্ষণ করা। আপনি যদি "সোমবার 09:00 America/New_York" একটি timestamptz হিসেবে সংরক্ষণ করেন, আপনি ইতোমধ্যেই একটি নির্দিষ্ট তারিখ (এবং DST অবস্থা) বেছে নিয়েছেন। ভবিষ্যত সোমগুলো জেনারেট করার সময় আসল উদ্দেশ্য ("সবসময় লোকালি 09:00") চলে যায়।\n\nআরেকটি সাধারণ কারণ হল স্থির UTC offsets (-05:00) নির্ভর করা। offsets-এ DST নিয়ম থাকে না। America/New_York এর মত IANA zone ID সংরক্ষণ করুন এবং PostgreSQL-কে প্রতিটি তারিখে সঠিক নিয়ম প্রয়োগ করতে দিন।\n\nকখন রূপান্তর করবেন সে ব্যাপারে সতর্ক থাকুন। যদি আপনি এক্সপ্যান্সন করার সময় খুব দ্রুত UTC তে রূপান্তর করেন, আপনি একটি DST অফসেট স্থির করে সেটি প্রতিটি occurrence-তে প্রয়োগ করতে পারেন। নিরাপদ প্যাটার্ন হল: occurrences লোকাল শব্দে (তারিখ + লোকাল সময় + জোন) জেনারেট করুন, তারপর প্রতিটি occurrence আলাদা করে একটি ইনস্ট্যান্টে রূপান্তর করুন।\n\nঘটনাগুলো যা বারংবার দেখা যায়:\n\n- পুনরাবৃত্ত লোকাল টাইম-অফ-ডে timestamptz হিসেবে সংরক্ষণ করা (আপনি time + tzid + একটি রুল দরকার ছিল)।\n- শুধু একটি অফসেট সংরক্ষণ করা, IANA জোন না রাখা।\n- রূপান্তর এক্সপ্যানশন করার সময় করা বরং শেষে না করা।\n- "চিরদিন" রুল এক্সপ্যান্ড করা বিনা কঠোর টাইম উইন্ডো ছাড়া।\n- DST শুরু ও DST শেষ সপ্তাহগুলো টেস্ট না করা।\n\nএকটি সরল টেস্ট যা অধিকাংশ সমস্যা ধরবে: একটি DST-যুক্ত জোন নিন, একটি সাপ্তাহিক 09:00 শিফট তৈরি করুন, এবং এমন দুটি মাসের ক্যালেন্ডার রেন্ডার করুন যা DST পরিবর্তন ক্রস করে। নিশ্চিত করুন প্রতিটি ইনস্ট্যান্স লোকালি 09:00 দেখায়, যদিও আন্ডারলাইং UTC ইনস্ট্যান্টগুলো ভিন্ন।\n\n## চালানোর আগে দ্রুত চেকলিস্ট\n\nরিলিজের আগে মৌলিক বিষয়গুলো পরীক্ষা করুন:\n\n- প্রতিটি শিডিউল একটি স্থানের (বা বিজনেস ইউনিটের) সাথে যুক্ত এবং তাতে একটি নামকৃত টাইমজোন স্টোর করা আছে।\n- আপনি IANA জোন ID (যেমন America/New_York) সংরক্ষণ করছেন, কাঁচা অফসেট নয়।\n- পুনরাবৃত্তি এক্সপ্যানশন শুধুমাত্র অনুরোধকৃত রেঞ্জের মধ্যে ঘটে।\n- ব্যতিক্রম ও ওভাররাইডগুলোর একটি একক, ডকুমেন্টেড প্রাধান্য ক্রমানুসারে আছে।\n- আপনি DST পরিবর্তন সপ্তাহগুলো এবং শিডিউল থেকে ভিন্ন টাইমজোনের একজন ভিউয়ারকে টেস্ট করেছেন।\n\nএকটি বাস্তবসম্মত ড্রাই রান করুন: একটি দোকান Europe/Berlin-এ প্রতি সপ্তাহে 09:00 লোকাল শিফট আছে। একটি ম্যানেজার America/Los_Angeles থেকে দেখে। নিশ্চিত করুন শিফট প্রতিটি সপ্তাহে Berlin টাইমে 09:00 থাকে, এমনকি যখন প্রতিটি অঞ্চল ভিন্ন সময়ে DST পরিবর্তন করে।\n\n## উদাহরণ: সাপ্তাহিক স্টাফ শিফট এক ছুটি ও DST পরিবর্তনসহ\n\nএকটি ছোট ক্লিনিক একটি পুনরাবৃত্ত শিফট চালায়: প্রতিটি সোমবার, 09:00 থেকে 17:00 ক্লিনিকের লোকাল টাইমে (America/New_York)। ক্লিনিক এক বিশেষ সোমবার বন্ধ আছে (ছুটি)। একজন স্টাফ দুই সপ্তাহ ইউরোপে ভ্রমণ করছে, কিন্তু ক্লিনিকের শিডিউল কর্মচারীর বর্তমান অবস্থানের উপর নয়, ক্লিনিকের ওয়াল-ঘড়ির উপর অ্যাঙ্কর করা থাকা উচিত।\n\nএটি সঠিকভাবে কাজ করানোর জন্য:\n\n- লোকাল তারিখে অ্যাঙ্কর করা একটি রিকারেন্স রুল সংরক্ষণ করুন (weekday = Monday, লোকাল সময় = 09:00-17:00)\n- শিডিউলের টাইমজোন সংরক্ষণ করুন (America/New_York)\n- রুলকে একটি কার্যকর start date দিন যাতে স্পষ্ট অ্যাঙ্কর থাকে\n- একটি ব্যতিক্রম সংরক্ষণ করুন যা সেই ছুটি সোমবার বাতিল করে (এবং এক-বারের জন্য পরিবর্তনগুলোর জন্য ওভাররাইড)\n\nএখন একটি দুই সপ্তাহের ক্যালেন্ডার রেঞ্জ রেন্ডার করুন যা New York-এ একটি DST পরিবর্তন অন্তর্ভুক্ত করে। কুয়েরি ঐ লোকাল তারিখ রেঞ্জে সোমবারগুলো জেনারেট করে, ক্লিনিকের লোকাল সময় যুক্ত করে, তারপর প্রতিটি occurrence-কে একটি অ্যাবসলুট ইনস্ট্যান্ট (timestamptz) এ রূপান্তর করে। যেহেতু রূপান্তর প্রতিটি occurrence-এ আলাদা করে করা হচ্ছে, DST সঠিকভাবে হ্যান্ডল করা হয়।\n\nভিন্ন ভিউয়াররা একই ইনস্ট্যান্টের জন্য ভিন্ন লোকাল ক্লক টাইম দেখবে:\n\n- Los Angeles-এ থাকা ম্যানেজারটি ঘড়িতে এটি আগে দেখবে।\n- Berlin-এ ভ্রমণরত স্টাফ এটি পরে দেখবে।\n\nক্লিনিক যা চেয়েছিল সেটা বজায় থাকে: প্রতিটি বাতিল না করা সোমবারে New York টাইমে 09:00 থেকে 17:00।\n\n## পরবর্তী ধাপ: বাস্তবায়ন, টেস্ট, এবং রক্ষণযোগ্যতা বজায় রাখুন\n\nআপনার টাইম আপ্রোচ প্রথমে তালাবদ্ধ করুন: আপনি কেবল রুল সংরক্ষণ করবেন, কেবল occurrence সংরক্ষণ করবেন, না কি হাইব্রিড ব্যবহার করবেন? অনেক বুকিং ও শিফট প্রোডাক্টের জন্য একটি হাইব্রিড ভালো: রুলকে সোর্স অব ট্রুথ রাখুন, প্রয়োজন হলে রোলিং ক্যাশ রাখুন, এবং ব্যতিক্রম ও ওভাররাইডগুলো বাস্তব রো হিসেবে সংরক্ষণ করুন।\n\nআপনার "টাইম কনট্রাক্ট" এক জায়গায় লিখে রাখুন: কী ইনস্ট্যান্ট হিসেবে গণ্য, কী লোকাল ওয়াল টাইম হিসেবে গণ্য, এবং কোন কলাম কোনটি সংরক্ষণ করে। এটি প্রতিরোধ করে যে কোনো এক এন্ডপয়েন্ট লোকাল সময় ফিরায় আর অন্যটি UTC।\n\nরিকারেন্স জেনারেশনকে একটি মডিউল রাখুন, ছড়ানো SQL টুকরো না। যদি কখনো আপনি "লোকালি 9:00 AM" কিভাবে ইন্টারপ্রেট করবেন তা বদলান, তখন একটি জায়গা আপডেট করলেই হবে।\n\nযদি আপনি সবকিছুকে নিজে কোড না করে কোনো scheduling টুল বানাচ্ছেন, AppMaster (appmaster.io) এই ধরনের কাজের জন্য বাস্তবসম্মত। আপনি Data Designer-এ ডাটাবেস মডেল করতে পারবেন, বিজনেস প্রসেসে রিকারেন্স ও ব্যতিক্রম লজিক বানাতে পারবেন, এবং তবুও শেষ পর্যন্ত বাস্তব জেনারেটেড ব্যাকএন্ড ও অ্যাপ কোড পেতে পারবেন।বিনামূল্যের পরিকল্পনা সহ অ্যাপমাস্টারের সাথে পরীক্ষা করুন।
আপনি যখন প্রস্তুত হবেন তখন আপনি সঠিক সদস্যতা বেছে নিতে পারেন৷