Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

SQL (PostgreSQL, MySQL, SQLite)  Module 3 – Data types and built-in functions

Dates, times and date arithmetic

Store and compare dates in PostgreSQL, MySQL and SQLite, add days and months, measure differences, extract weekdays and ISO weeks, and label financial years.

  • Beginner
  • 30 minutes
  • Examples run with sql.js 1.14.2
  • By MySmartCoPilot
Free: 30 minutes

What you will learn

  • Store and compare dates in each engine
  • Add and subtract days, months and intervals
  • Extract parts such as year, month, ISO week and weekday, and format dates
  • Label dates with a financial year that starts in April, and its quarter

Before you start

What you’ll learn · Before you start

What you will learn

  • Store and compare dates in each engine
  • Add and subtract days, months and intervals
  • Extract parts such as year, month, ISO week and weekday, and format dates
  • Label dates with a financial year that starts in April, and its quarter

Before you start

On this page

Dates look simple until you compute with them. PostgreSQL and MySQL have real date types (date, timestamp, DATETIME); SQLite has none and stores dates as text in the ISO 8601 form YYYY-MM-DD, which its date functions read and write. Adding days is easy everywhere. Adding a month is not, because “one month after the 31st” has no single answer and the engines choose differently. Weekday numbers differ too: Sunday is 0 in one function, 1 in another and 7 in a third. This lesson stores, compares, shifts, measures and labels dates, including the Indian financial year, which starts in April.

All the date queries run in the browser’s SQLite 3.49.1 (sql.js 1.14.2), and the PostgreSQL 18 and MySQL 8.4 equivalents in the tables come from their manuals.

Storing dates

Date and time storage in PostgreSQL, MySQL and SQLite
Criterion PostgreSQL 18MySQL 8.4SQLite 3.49
A calendar day dateDATETEXT 'YYYY-MM-DD'
A day and a time timestamp, timestamptzDATETIME, TIMESTAMPTEXT 'YYYY-MM-DD HH:MM:SS'
Other forms time, intervalTIME, YEARREAL Julian day or INTEGER Unix seconds
Today CURRENT_DATECURDATE()date('now')

In SQLite the text form works because ISO 8601 writes the year, then the month, then the day, each with a fixed number of digits, so comparing two such strings character by character gives the same order as comparing the dates. That only holds for the exact form:

Padded and unpadded dates, compared as text SQL · iso_text.sql
-- SQLite stores dates as text. Zero-padded year-month-day text sorts and compares in date order.
SELECT '2026-03-05' < '2026-11-01' AS padded_order,
       '2026-3-5'   < '2026-11-01' AS unpadded_order,
       date('2026-03-05')          AS padded,
       date('2026-3-5')            AS unpadded,
       date('05/03/2026')          AS day_first;

-- Build the ISO form from its parts when the data arrives in pieces.
SELECT printf('%04d-%02d-%02d', 2026, 3, 5) AS built;

Output

┌──────────────┬────────────────┬────────────┬──────────┬───────────┐
│ padded_order │ unpadded_order │   padded   │ unpadded │ day_first │
├──────────────┼────────────────┼────────────┼──────────┼───────────┤
│ 1            │ 0              │ 2026-03-05 │          │           │
└──────────────┴────────────────┴────────────┴──────────┴───────────┘
┌────────────┐
│   built    │
├────────────┤
│ 2026-03-05 │
└────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < iso_text.sql

'2026-3-5' sorts after '2026-11-01', because its sixth character, 3, is larger than 1, and date() does not recognise it at all (NULL). A day-first date such as '05/03/2026' is not understood either. Store every date as zero-padded YYYY-MM-DD text, convert other formats once when the data arrives, and printf('%04d-%02d-%02d', …) builds the form from separate numbers. SQLite’s date('now') gives today’s date in UTC; the next lesson explains why that is usually what you want.

Parts of a date

strftime(format, date) extracts parts, and a few of its codes matter for reports:

Weekday numbers, day of the year and ISO weeks SQL · parts.sql
-- Parts of a date with strftime(). SQLite has no weekday names, so substr() picks one from a string.
CREATE TABLE days (d TEXT);
INSERT INTO days VALUES ('2026-03-29'), ('2026-03-30'), ('2026-12-31'), ('2027-01-01'), ('2027-01-04');

SELECT d,
       substr('SunMonTueWedThuFriSat', 1 + 3 * strftime('%w', d), 3) AS day,
       strftime('%w', d)     AS w,
       strftime('%u', d)     AS u,
       strftime('%j', d)     AS day_of_year,
       strftime('%G-W%V', d) AS iso_week
FROM days;

Output

┌────────────┬─────┬───┬───┬─────────────┬──────────┐
│     d      │ day │ w │ u │ day_of_year │ iso_week │
├────────────┼─────┼───┼───┼─────────────┼──────────┤
│ 2026-03-29 │ Sun │ 0 │ 7 │ 088         │ 2026-W13 │
│ 2026-03-30 │ Mon │ 1 │ 1 │ 089         │ 2026-W14 │
│ 2026-12-31 │ Thu │ 4 │ 4 │ 365         │ 2026-W53 │
│ 2027-01-01 │ Fri │ 5 │ 5 │ 001         │ 2026-W53 │
│ 2027-01-04 │ Mon │ 1 │ 1 │ 004         │ 2027-W01 │
└────────────┴─────┴───┴───┴─────────────┴──────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < parts.sql

%w numbers the days from Sunday = 0 and %u from Monday = 1 to Sunday = 7, the ISO 8601 numbering. %j is the day of the year. %G-W%V is the ISO 8601 week: weeks start on Monday, and week 1 is the one that contains the year’s first Thursday, so the first days of a January can belong to week 53 of the year before, as the second-last row shows. SQLite has no weekday names, so the query picks three letters out of a string with substr().

Grouping on %u gives a weekly report that starts on Monday:

Orders per weekday, Monday first SQL · orders_per_weekday.sql
-- Orders per weekday, Monday first: group on the ISO day number (%u: Monday = 1, Sunday = 7).
CREATE TABLE orders (id INTEGER PRIMARY KEY, placed_on TEXT);
INSERT INTO orders (placed_on) VALUES
  ('2026-03-02'), ('2026-03-02'), ('2026-03-04'), ('2026-03-06'), ('2026-03-07'),
  ('2026-03-07'), ('2026-03-08'), ('2026-03-08'), ('2026-03-08'), ('2026-03-09');

SELECT strftime('%u', placed_on)                                     AS iso_day,
       substr('MonTueWedThuFriSatSun', 3 * strftime('%u', placed_on) - 2, 3) AS day,
       count(*)                                                      AS orders
FROM orders
GROUP BY iso_day
ORDER BY iso_day;

Output

┌─────────┬─────┬────────┐
│ iso_day │ day │ orders │
├─────────┼─────┼────────┤
│ 1       │ Mon │ 3      │
│ 3       │ Wed │ 1      │
│ 5       │ Fri │ 1      │
│ 6       │ Sat │ 2      │
│ 7       │ Sun │ 3      │
└─────────┴─────┴────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < orders_per_weekday.sql

Each engine numbers weekdays its own way, which is a classic source of off-by-one bugs when a query moves:

Weekday numbers and ISO weeks
Criterion PostgreSQL 18MySQL 8.4SQLite 3.49
Sunday = 0 … Saturday = 6 extract(dow FROM d)DATE_FORMAT(d, '%w')strftime('%w', d)
Monday = 1 … Sunday = 7 extract(isodow FROM d)WEEKDAY(d) + 1strftime('%u', d)
Sunday = 1 … Saturday = 7 NoneDAYOFWEEK(d)None
ISO week label to_char(d, 'IYYY-"W"IW')DATE_FORMAT(d, '%x-W%v')strftime('%G-W%V', d)

MySQL’s WEEKDAY() itself starts at Monday = 0, hence the + 1. One more trap hides in the last row. In SQLite, as in C, %V is the ISO week, but in MySQL’s DATE_FORMAT the same %V is a week that starts on Sunday; MySQL’s ISO week is %v with the year %x.

Adding days and months

SQLite shifts dates with modifiers such as '+10 days', '-1 month' or '+1 year', applied from left to right. Days are always clean. Months are where the engines part ways:

One month after the 15th, the 31st and a leap day SQL · add_months.sql
-- Adding a month. When the day does not exist in the new month, SQLite rolls over into the next one,
-- unless the 'floor' modifier asks for the last day of the new month instead.
CREATE TABLE starts (d TEXT);
INSERT INTO starts VALUES ('2026-01-15'), ('2026-01-31'), ('2026-03-31'), ('2026-05-31');

SELECT d,
       date(d, '+1 month')          AS plus_one_month,
       date(d, '+1 month', 'floor') AS with_floor
FROM starts;

-- A year after a leap day, and the first and last day of a month.
SELECT date('2024-02-29', '+1 year')                             AS after_leap_day,
       date('2024-02-29', '+1 year', 'floor')                    AS leap_floor,
       date('2026-02-17', 'start of month')                      AS first_day,
       date('2026-02-17', 'start of month', '+1 month', '-1 day') AS last_day;

Output

┌────────────┬────────────────┬────────────┐
│     d      │ plus_one_month │ with_floor │
├────────────┼────────────────┼────────────┤
│ 2026-01-15 │ 2026-02-15     │ 2026-02-15 │
│ 2026-01-31 │ 2026-03-03     │ 2026-02-28 │
│ 2026-03-31 │ 2026-05-01     │ 2026-04-30 │
│ 2026-05-31 │ 2026-07-01     │ 2026-06-30 │
└────────────┴────────────────┴────────────┘
┌────────────────┬────────────┬────────────┬────────────┐
│ after_leap_day │ leap_floor │ first_day  │  last_day  │
├────────────────┼────────────┼────────────┼────────────┤
│ 2025-03-01     │ 2025-02-28 │ 2026-02-01 │ 2026-02-28 │
└────────────────┴────────────┴────────────┴────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < add_months.sql

One month after 2026-01-31 would need a 31st day in February, which does not exist, so SQLite counts on into March and returns 2026-03-03. The floor modifier, new in SQLite 3.46.0, resolves the same case to the last day of the new month instead, 2026-02-28; ceiling is the default. A year after a leap day behaves the same way. The second query shows the first and the last day of a month, built from start of month, +1 month and -1 day.

PostgreSQL and MySQL always choose the last day of the shorter month: in PostgreSQL date '2026-01-31' + interval '1 month' is 2026-02-28 00:00:00, and in MySQL DATE_ADD('2026-01-31', INTERVAL 1 MONTH) is 2026-02-28. So the same “due one month after the invoice” query can give a different date in SQLite. Decide the rule your business wants for the 29th to the 31st, write it down, and test it on those days. For the last day of a month, MySQL has LAST_DAY(d); in PostgreSQL write (date_trunc('month', d) + interval '1 month' - interval '1 day')::date.

Shifting dates by days and months
Criterion PostgreSQL 18MySQL 8.4SQLite 3.49
Add ten days d + 10DATE_ADD(d, INTERVAL 10 DAY)date(d, '+10 days')
Add one month d + interval '1 month'DATE_ADD(d, INTERVAL 1 MONTH)date(d, '+1 month')
A month after a 31st Last day of the next monthLast day of the next monthRolls over, unless 'floor'
Last day of the month date_trunc() and intervalsLAST_DAY(d)'start of month' modifiers

Differences between dates

How long two deliveries took SQL · differences.sql
-- How long a delivery took: calendar days between the dates, and exact hours from Unix seconds.
CREATE TABLE deliveries (id INTEGER, placed_at TEXT, delivered_at TEXT);
INSERT INTO deliveries VALUES
  (1, '2026-03-30 09:15:00', '2026-03-31 18:45:00'),
  (2, '2026-03-31 22:10:00', '2026-04-02 08:40:00');

SELECT id,
       CAST(julianday(date(delivered_at)) - julianday(date(placed_at)) AS INTEGER) AS calendar_days,
       (unixepoch(delivered_at) - unixepoch(placed_at)) / 3600.0                    AS hours,
       timediff(delivered_at, placed_at)                                            AS as_text
FROM deliveries;

Output

┌────┬───────────────┬───────┬──────────────────────────┐
│ id │ calendar_days │ hours │         as_text          │
├────┼───────────────┼───────┼──────────────────────────┤
│ 1  │ 1             │ 33.5  │ +0000-00-01 09:30:00.000 │
│ 2  │ 2             │ 34.5  │ +0000-00-01 10:30:00.000 │
└────┴───────────────┴───────┴──────────────────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < differences.sql

julianday() turns a date into a day number, so the difference of two of them counts days; taking date() first counts calendar days, which is what “delivered the next day” means. unixepoch() gives whole seconds, so dividing the difference by 3600.0 gives exact hours, 33.5 and 34.5 here. timediff(), new in SQLite 3.43.0, writes the gap as years, months, days and a time, which reads well but is text you cannot add up.

In PostgreSQL, subtracting one date from another gives an integer number of days, subtracting timestamps gives an interval, and age() expresses a gap in years, months and days. MySQL has DATEDIFF(later, earlier) for days and TIMESTAMPDIFF(HOUR, earlier, later) for any unit, which counts whole units only. Mind the argument orders: MySQL’s DATEDIFF puts the later date first and TIMESTAMPDIFF puts it last.

Financial years and quarters

The Indian financial year

The General Clauses Act, 1897, defines India’s financial year as the year that begins on the first day of April, so FY2026-27 runs from 2026-04-01 to 2027-03-31. Reports, tax returns and company accounts in India use it, and a query that groups by the calendar year splits each financial year in two.

FY2026-27 runs from April to March: Q1 to Q3 fall in calendar year 2026, Q4 (January to March) in 2027.FY2026-27from April to MarchCalendar year 2026Calendar year 2027Q1: April to JuneQ2: July to SeptemberQ3: October to DecemberQ4: January to Marchstarts in Aprilthe calendar year changesinside the financial year

The quarters of one Indian financial year

Text description of the diagram

The diagram shows the financial year FY2026-27, which runs from April to March, as two boxes, one per calendar year.

  1. Calendar year 2026 holds the first three quarters: Q1 from April to June, Q2 from July to September and Q3 from October to December.
  2. Calendar year 2027 holds the last quarter, Q4, from January to March.

The arrow between the boxes says that the calendar year changes inside the financial year. That is why the year of a date alone cannot label its financial year: January to March belong to the financial year that started in the previous calendar year, and moving a date back by three months gives the start year directly.

Moving a date back by three months turns April into January, so the year of the moved date is the year in which its financial year began:

The financial year from a three-month shift SQL · financial_year.sql
-- A financial year that starts in April: move the date back 3 months, and its year is the start year.
CREATE TABLE orders (id INTEGER, ordered_on TEXT);
INSERT INTO orders VALUES (1, '2026-03-31'), (2, '2026-04-01'), (3, '2026-12-31'), (4, '2027-01-01');

SELECT id,
       ordered_on,
       CAST(strftime('%Y', ordered_on, '-3 months') AS INTEGER) AS fy_start,
       strftime('%m', ordered_on, '-3 months')                  AS shifted_month,
       strftime('%m', ordered_on, '-3 months', 'floor')         AS with_floor
FROM orders;

Output

┌────┬────────────┬──────────┬───────────────┬────────────┐
│ id │ ordered_on │ fy_start │ shifted_month │ with_floor │
├────┼────────────┼──────────┼───────────────┼────────────┤
│ 1  │ 2026-03-31 │ 2025     │ 12            │ 12         │
│ 2  │ 2026-04-01 │ 2026     │ 01            │ 01         │
│ 3  │ 2026-12-31 │ 2026     │ 10            │ 09         │
│ 4  │ 2027-01-01 │ 2026     │ 10            │ 10         │
└────┴────────────┴──────────┴───────────────┴────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < financial_year.sql

fy_start is right for all four orders: the last day of March belongs to the year that began a year earlier, and the first of April starts a new one. The shifted_month column shows the catch. 2026-12-31 moved back three months would need a 31st day in September, which rolls over to the first of October, so the month says 10 where 9 was meant. The year is never affected, because December has 31 days, but anything computed from the shifted month, such as a quarter, would be wrong for that day. Adding 'floor' after the shift fixes it, and so does working from the original month number. The exercise below builds both labels.

Formatting dates for people

Keep dates as ISO text or a date type for storing, sorting and comparing, and format them only for display. In SQLite strftime('%d/%m/%Y', d) writes the day first, as most Indian forms do; PostgreSQL uses to_char(d, 'DD/MM/YYYY') and MySQL DATE_FORMAT(d, '%d/%m/%Y'). A formatted date is text again: sorted, '01/04/2026' comes before '31/03/2026'.

Date Calculator Add days or months to a date and count the days between two dates, to check a query's answer.

Your turn

Exercise · Medium · SQL

Label orders with their financial year and quarter

India's financial year begins in April and ends in March. The table orders has the columns id and ordered_on, a date written as YYYY-MM-DD text.

Write a query that returns each order's id with two labels:

- fy: the financial year, written FY followed by its start year, a hyphen and the last two digits of its end year. An order on 2026-03-31 is in FY2025-26, one on 2026-04-01 in FY2026-27; - quarter: Q1 for April to June, Q2 for July to September, Q3 for October to December and Q4 for January to March.

The order of the rows does not matter. The sample tests cover the last day of March, the first day of April, the turn of the calendar year, the last day of a month and a financial year that ends in a new century.

Starter code · fy_labels.sql

-- fy: 'FY' || start year || '-' || last two digits of the end year (April to March);
-- quarter: Q1 = April-June, Q2 = July-September, Q3 = October-December, Q4 = January-March.
SELECT id,
       'FY' || strftime('%Y', ordered_on) AS fy,
       'Q1' AS quarter
FROM orders;
The sample tests · tests.yaml
# Sample tests: each one builds a small orders table, runs your query on it and compares the rows it returns (in any
# order).
tests:
  - name: puts the last day of March and the first day of April in different years
    setup: |
      CREATE TABLE orders (id INTEGER PRIMARY KEY, ordered_on TEXT NOT NULL);
      INSERT INTO orders VALUES (1, '2026-03-31'), (2, '2026-04-01'), (3, '2026-06-30'), (4, '2026-07-01');
    expected:
      columns: [id, fy, quarter]
      rows:
        - [1, 'FY2025-26', 'Q4']
        - [2, 'FY2026-27', 'Q1']
        - [3, 'FY2026-27', 'Q1']
        - [4, 'FY2026-27', 'Q2']
  - name: keeps the turn of the calendar year inside one financial year
    setup: |
      CREATE TABLE orders (id INTEGER PRIMARY KEY, ordered_on TEXT NOT NULL);
      INSERT INTO orders VALUES (1, '2026-09-30'), (2, '2026-10-01'), (3, '2026-12-31'), (4, '2027-01-01'), (5, '2027-03-31');
    expected:
      columns: [id, fy, quarter]
      rows:
        - [1, 'FY2026-27', 'Q2']
        - [2, 'FY2026-27', 'Q3']
        - [3, 'FY2026-27', 'Q3']
        - [4, 'FY2026-27', 'Q4']
        - [5, 'FY2026-27', 'Q4']
  - name: handles the 31st of a month and a year that ends in a new century
    setup: |
      CREATE TABLE orders (id INTEGER PRIMARY KEY, ordered_on TEXT NOT NULL);
      INSERT INTO orders VALUES (1, '2026-08-31'), (2, '2099-05-31'), (3, '2100-02-28'), (4, '2024-02-29');
    expected:
      columns: [id, fy, quarter]
      rows:
        - [1, 'FY2026-27', 'Q2']
        - [2, 'FY2099-00', 'Q1']
        - [3, 'FY2099-00', 'Q4']
        - [4, 'FY2023-24', 'Q4']
A hint

Moving a date back by three months turns its year into the financial year's start year: strftime('%Y', ordered_on, '-3 months'). For the quarter, work from the original month number, CAST(strftime('%m', ordered_on) AS INTEGER), with a CASE, or add 'floor' after the shift so that a day such as the 31st cannot roll into the next month. substr(start_year + 1, 3) gives the last two digits of the end year.

The sample tests run on this device, in your browser (sql.js): nothing is sent to mysmartcopilot.com. The first run downloads SQLite (about 0.7 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 What does this comparison return in SQLite?

    Read the code, then choose one answer.

    SELECT '2026-3-5' < '2026-11-01' AS earlier;
    Show the answer to question 1

    Answer: 0, because the strings are compared as text and '3' is larger than '1'

    Two strings are compared character by character. The first difference is the sixth character, 3 against 1, so the unpadded date sorts later. Zero-padded YYYY-MM-DD text avoids this.

  2. Question 2 of 6 What does this query return in SQLite 3.49?

    Read the code, then choose one answer.

    SELECT date('2026-01-31', '+1 month') AS d;
    Show the answer to question 2

    Answer: The third day of March

    SQLite first builds an impossible date, day 31 in February, and then counts on into March, which gives the third day of March. With the floor modifier after the shift, the same expression returns the last day of February, which is also what PostgreSQL and MySQL return.

  3. Question 3 of 6 In SQLite, which strftime code numbers Monday as 1 and Sunday as 7?

    Choose one answer.

    Show the answer to question 3

    Answer: %u

    %u is the ISO 8601 weekday, Monday = 1 to Sunday = 7. %w numbers Sunday as 0, %V is the ISO week of the year and %j the day of the year.

  4. Question 4 of 6 The first day of a year is a Friday. To which ISO 8601 week does it belong?

    Choose one answer.

    Show the answer to question 4

    Answer: The last week of the previous year (52 or 53)

    ISO week 1 is the week that contains the year's first Thursday. A Friday first day falls in the week that contains the previous Thursday, which belongs to the old year; strftime('%G-W%V') shows the year of the week too.

  5. Question 5 of 6 An order is placed on the last day of March of the calendar year 2027. Which financial year is it in, for a year that starts in April?

    Choose one answer.

    Show the answer to question 5

    Answer: FY2026-27

    The financial year that ends with that March began in April of the previous calendar year. Moving the date back three months gives a December date of the calendar year 2026, which is the start year.

  6. Question 6 of 6 How many hours does this query return in SQLite?

    Read the code, then choose one answer.

    SELECT (unixepoch('2026-03-31 18:45:00') - unixepoch('2026-03-30 09:15:00')) / 3600.0 AS hours;
    Show the answer to question 6

    Answer: 33.5

    From 09:15 to 09:15 on the next day is 24 hours, and from 09:15 to 18:45 is 9.5 more: 33.5 hours. unixepoch() gives whole seconds, so the difference divided by 3600.0 is exact.

Interview questions

Fresher · Why do SQLite queries store dates as YYYY-MM-DD text, and what goes wrong with other formats?

SQLite has no date type, so a date is stored as text, as a Julian day number or as Unix seconds, and its date functions understand all three. ISO 8601 text such as 2026-03-05 is the readable choice that still behaves like a date: the year comes first, then the month, then the day, each zero-padded, so ordinary text comparison sorts it in date order and BETWEEN, MIN() and ORDER BY work. A day-first form such as 05/03/2026 sorts by day before month, and an unpadded one such as 2026-3-5 sorts after 2026-11-01; date() returns NULL for both. Convert other formats once, when the data is loaded, and format dates for people only at the end.

Key takeaways

  • PostgreSQL and MySQL have date types; SQLite stores dates as zero-padded YYYY-MM-DD text, which sorts correctly only in exactly that form.
  • strftime('%w') numbers Sunday 0, %u numbers Monday 1 to Sunday 7, and %G-W%V is the ISO week in SQLite, but %V means a Sunday week in MySQL.
  • One month after the 31st is the last day of the next month in PostgreSQL and MySQL, but rolls into the month after in SQLite unless you add floor.
  • Count days with julianday() differences and exact hours with unixepoch() differences; timediff() is for reading.
  • A financial year that starts in April is the year of the date moved back three months; take the quarter from the original month.
  • Format dates only for display, because formatted dates are text that sorts by its first characters.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.