[Lập trình C] In lịch của một năm bất kỳ bằng công thức Zeller

Saturday, May 16, 2020
Edit this post

https://pixabay.com/photos/calendar-pay-number-year-date-2428560/

Viết chương trình nhập vào một năm (> 1582), in lịch của năm đó (với ngày đầu tuần là Chủ nhật). Tính thứ cho ngày đầu năm bằng công thức Zeller.

Kết quả chạy chương trình


Input year: 2020

January
  S  M  T  W  T  F  S
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30 31

February
  S  M  T  W  T  F  S
                    1
  2  3  4  5  6  7  8
  9 10 11 12 13 14 15
 16 17 18 19 20 21 22
 23 24 25 26 27 28 29


March
  S  M  T  W  T  F  S
  1  2  3  4  5  6  7
  8  9 10 11 12 13 14
 15 16 17 18 19 20 21
 22 23 24 25 26 27 28
 29 30 31

April
  S  M  T  W  T  F  S
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30

May
  S  M  T  W  T  F  S
                 1  2
  3  4  5  6  7  8  9
 10 11 12 13 14 15 16
 17 18 19 20 21 22 23
 24 25 26 27 28 29 30
 31

June
  S  M  T  W  T  F  S
     1  2  3  4  5  6
  7  8  9 10 11 12 13
 14 15 16 17 18 19 20
 21 22 23 24 25 26 27
 28 29 30

July
  S  M  T  W  T  F  S
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30 31

August
  S  M  T  W  T  F  S
                    1
  2  3  4  5  6  7  8
  9 10 11 12 13 14 15
 16 17 18 19 20 21 22
 23 24 25 26 27 28 29
 30 31

September
  S  M  T  W  T  F  S
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30

October
  S  M  T  W  T  F  S
              1  2  3
  4  5  6  7  8  9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26 27 28 29 30 31

November
  S  M  T  W  T  F  S
  1  2  3  4  5  6  7
  8  9 10 11 12 13 14
 15 16 17 18 19 20 21
 22 23 24 25 26 27 28
 29 30

December
  S  M  T  W  T  F  S
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30 31

Lịch Gregorian

Trước khi bắt tay vào giải bài tập, chúng ta cần hiểu đây là loại lịch nào. Với thông tin năm nhập vào > 1582, có thể đoán đây là lịch Gregorian (Gregorian Calendar). Vậy lịch Gregorian là gì?

Theo Wikpedia định nghĩa:

Lịch Gregorius, còn gọi là Tây lịch, Công lịch, Dương lịch, là một bộ lịch do Giáo hoàng Grêgôriô XIII đưa ra vào năm 1582. Lịch Gregorius chia thành 12 tháng với 365 ngày, cứ 4 năm thì thêm một ngày vào cuối tháng 2 tạo thành năm nhuận (366 ngày). Trước đó lịch Julius quy ước một năm có 365,25 ngày, song độ dài của năm mặt trời là 365,242216 ngày cho nên một năm theo lịch Julius dài hơn khoảng 0,0078 ngày so với năm mặt trời (tức là khoảng 11 phút 14 giây).

Vậy, lịch Gregorian chính là Dương lịch mà chúng ta đang sử dụng phổ biến, khác với Âm lịch hay Phật lịch.

Công thức Zeller

Công thức Zeller là một giải thuật được sáng chế bởi Christian Zeller để tính thứ cho bất kỳ ngày nào theo lịch Julian hoặc Gregorian.

Dưới đây là công thức Zeller cho lịch Gregorian:


Còn đây là công thức Zeller cho lịch Julian:


Trong đó:

- h là thứ trong tuần (day-of-week) (0 = Thứ bảy, 1 = Chủ nhật, 2 = Thứ hai, ..., 6 = Thứ sáu).
- q là ngày trong tháng.
- m là tháng (3 = Tháng ba, 4 = Tháng tư, 5 = tháng 5, ..., 14 = Tháng hai).
- K là năm của thế kỷ (year mod 100).
- J là thế kỷ (year div 100).

Chú thích:

- div: chia lấy nguyên. Vd: 5 / 2 = 2.
- mod: chia lấy dư. Vd: 5 % 2 = 1.
- Trong giải thuật này thì Tháng một và Tháng hai là được tính là tháng 13 và 14 của năm trước đó. Ví dụ như ngày 2 tháng 2 năm 2010 đối với giải thuật này sẽ được xem là ngày thứ hai của tháng 14 của năm 2009 (02/14/2009, định dạng DD/MM/YYYY).
- Đối với ISO day-of-week (1 = Thứ hai, 7 = Thứ bảy), có thể sử dụng công thức này để chuyển đổi: d = ((h + 5) mod 7) + 1
- Theo cách tính modulus thông thường cho số âm thì -2 mod 7 sẽ được tính bằng cách như sau: Gọi -2 là số bị chia, 7 là số chia, lấy số chia nhân với một số nguyên âm bất kỳ sao cho kết quả là một số âm x lớn nhất nhưng vẫn nhỏ hơn số bị chia. Trong trường hợp này, ta có: 7 * (-1) = -7. -7 là số âm lớn nhất có thể nhưng vẫn nhỏ hơn -2. Giả sử lấy 7 * (-2) = -14. -14 dù < -2 nhưng không phải là số âm lớn nhất so với -7. Sau đó lấy số bị chia cộng với trị tuyệt đối của kết quả trên ta được: -2 + |7 * (-1)| = 5. Vậy -2 mod 7 = 5. Tương tự -2 mod 9 sẽ bằng - 2 + |9 * (-1)| = 7. Hoặc -11 mod 9 = -11 + |9 * (-2)| = -11 + 18 = 7.
- Tuy nhiên, Phần lớn các ngôn ngữ máy tính sẽ trả về -2 cho phép tính -2 mod 7, điều này có thể khiến cho kết quả của cuối cùng được trả về từ công thức Zeller là một số âm. Do đó, để đảm bảo kết quả luôn dương, chúng ta phải điều chỉnh công thức từ -2 mod 7 sang 5 mod 7 vì 5 mod 7 cũng bằng 5.

Vậy cuối cùng, cài đặt C của công thức Zeller cho lịch Gregorian như sau:


int getDow(int q, int m, int y) {
    if (m == 1) { m = 13; y--; }
    if (m == 2) { m = 14; y--; }
    int k = y % 100;
    int j = y / 100;
    int h = q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j;
    h = h % 7;
    return h == 0 ? 6 : h - 1;
}

Theo công thức Zeller gốc thì h = 0 là Thứ bảy. Nhưng vì tôi muốn lịch của tôi bắt đầu từ Chủ Nhật nên khi trả về h tôi kiểm tra nếu h = 0 thì tôi cho h = 6, với các giá trị khác của h thì tôi giảm đi 1 đơn vị để khớp với thứ tự {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}.

Lời giải

- Tôi sử dụng một vòng lặp để quét qua từng tháng một.
- Với mỗi tháng, tôi sẽ dùng công thức Zeller để tính thứ (day of week) của ngày 1 của tháng đó.
- Đồng thời tôi cũng cần xác định xem tháng đó có bao nhiêu ngày. Như chúng ta đã biết: Tháng 1,3,5,7,8,10,12 có 31 ngày; Tháng 4,6,9,11 có 30 ngày; Tháng 2 có thể có 28 hoặc 29 ngày tuỳ thuộc vào năm đó có phải là năm nhuận hay không.
- Một năm được gọi là năm nhuận nếu năm đó chia hết cho 4 và không chia hết cho 100 hoặc nếu năm đó chia hết cho 400.


#include <stdio.h>

int isLeapYear(int y) {
    return (!(y % 4) && y % 100) || !(y % 400);
}

int getDow(int q, int m, int y) {
    if (m == 1) { m = 13; y--; }
    if (m == 2) { m = 14; y--; }
    int k = y % 100;
    int j = y / 100;
    int h = q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j;
    h = h % 7;
    return h == 0 ? 6 : h - 1;
}

int main()
{
    char months[12][10] = {
        "January", "February", "March", "April", "May", "June", 
        "July", "August", "September", "October", "November", "December"
    };
    
    int y;
    printf("Input year: ");
    scanf("%d", &y);
    
    putchar('\n');
    
    for (int m = 1; m <= 12; m++, printf("\n\n")) {
        printf("%s\n", months[m - 1]);
        
        int top;
        switch(m) {
            case 2: top = isLeapYear(y) ? 29 : 28; break;
            case 4: case 6: case 9: case 11: top = 30; break;
            default: top = 31; break;
        }
        
        int dow = getDow(1, m, y);
        printf("  S  M  T  W  T  F  S\n");
        for (int i = 0; i < dow; i++) 
            printf("%3s", " ");
        for (int i = 1; i <= top; i++) {
            printf("%3d", i);
            if (i < top && (i + dow) % 7 == 0)
                printf("\n");
        }
    }
    
    return 0;
}

Sử dụng mảng

- Tôi sử dụng một mảng 2 chiều có kích thước cố định 6 x 7 để lưu trữ lịch. Khi in, những phần tử có giá trị '0' sẽ được thay bằng khoảng trắng. Lý do của cách làm này là để bài nâng cao bên dưới được dễ hơn, bỏ đi các logic phức tạp của việc in lịch theo chiều dọc.
- Như các bạn thấy tôi sử dụng con trỏ int* d trỏ vào phần tử đầu tiên của mảng 2 chiều days. Tại sao lại như vậy? Mảng 2 chiều int days[6][7] với kích thước cố định thực chất là 42 ô nhớ 4 bytes liền kề nhau. int days[6][7] có thể tạm coi là tương đương với int**. Do đó khi viết days[0] tức tương đương với int*. Vậy, khi tôi viết int* d = days[0] tức là tôi đang gán địa chỉ của ô nhớ đầu tiên trong mảng days cho con trỏ d, hay nói cách khác, d đang trỏ vào phần tử đầu tiên của mảng 2 chiều days. Như vậy, tôi sử dụng phương pháp duyệt mảng sử dụng con trỏ trung gian.
- Khi bạn sử dụng toán tử ++ đối với một con trỏ, thì điều đó có nghĩa là con trỏ sẽ trỏ qua ô nhớ kế tiếp.
- Và để truy xuất vào giá trị của ô nhớ mà con trỏ d đang trỏ tới, chúng ta dùng *d (ý nghĩa khác với int* d dùng để khai báo con trỏ). Còn khi chúng ta viết d không, tức là đang nói tới địa chỉ của ô nhớ mà con trỏ d đang trỏ tới.


#include <stdio.h>
#include <stdlib.h>

int isLeapYear(int y) {
    return (!(y % 4) && y % 100) || !(y % 400);
}

int getDow(int q, int m, int y) {
    if (m == 1) { m = 13; y--; }
    if (m == 2) { m = 14; y--; }
    int k = y % 100;
    int j = y / 100;
    int h = q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j;
    h = h % 7;
    return h == 0 ? 6 : h - 1;
}

int main()
{
    char months[12][10] = {
        "January", "February", "March", "April", "May", "June", 
        "July", "August", "September", "October", "November", "December"
    };
    char daysow[7][4] = {"S", "M", "T", "W", "T", "F", "S"};
    
    int y;
    printf("Input year: ");
    scanf("%d", &y);
    
    putchar('\n');
    
    for (int m = 1; m <= 12; m++, printf("\n")) {
        printf("%s\n", months[m - 1]);
        
        int top;
        switch(m) {
            case 2: top = isLeapYear(y) ? 29 : 28; break;
            case 4: case 6: case 9: case 11: top = 30; break;
            default: top = 31; break;
        }
        
        int dow = getDow(1, m, y);

        int days[6][7] = {0};
        int* d = days[0];
        for (int i = 0; i < dow; i++) d++;
        for (int i = 0; i < top; i++) *(d++) = i + 1;
        for (int i = 0; i < 7; i++) printf("%3s", daysow[i]);
        printf("\n");
        int row = dow + top > 35 ? 6 : 5;
        for (int i = 0; i < row; i++, putchar('\n'))
            for (int j = 0; j < 7; j++)
                days[i][j] == 0 ? printf("%3s", " ") : printf("%3d", days[i][j]);
            
    }
    
    return 0;
}

Bài nâng cao

Tương tự như bài trên nhưng lịch của mỗi tháng được in theo chiều dọc.


Input year: 1975

January
 S      5 12 19 26
 M      6 13 20 27
 T      7 14 21 28
 W   1  8 15 22 29
 T   2  9 16 23 30
 F   3 10 17 24 31
 S   4 11 18 25   

February
 S      2  9 16 23
 M      3 10 17 24
 T      4 11 18 25
 W      5 12 19 26
 T      6 13 20 27
 F      7 14 21 28
 S   1  8 15 22   

March
 S      2  9 16 23 30
 M      3 10 17 24 31
 T      4 11 18 25   
 W      5 12 19 26   
 T      6 13 20 27   
 F      7 14 21 28   
 S   1  8 15 22 29   

April
 S      6 13 20 27
 M      7 14 21 28
 T   1  8 15 22 29
 W   2  9 16 23 30
 T   3 10 17 24   
 F   4 11 18 25   
 S   5 12 19 26   

May
 S      4 11 18 25
 M      5 12 19 26
 T      6 13 20 27
 W      7 14 21 28
 T   1  8 15 22 29
 F   2  9 16 23 30
 S   3 10 17 24 31

June
 S   1  8 15 22 29
 M   2  9 16 23 30
 T   3 10 17 24   
 W   4 11 18 25   
 T   5 12 19 26   
 F   6 13 20 27   
 S   7 14 21 28   

July
 S      6 13 20 27
 M      7 14 21 28
 T   1  8 15 22 29
 W   2  9 16 23 30
 T   3 10 17 24 31
 F   4 11 18 25   
 S   5 12 19 26   

August
 S      3 10 17 24 31
 M      4 11 18 25   
 T      5 12 19 26   
 W      6 13 20 27   
 T      7 14 21 28   
 F   1  8 15 22 29   
 S   2  9 16 23 30   

September
 S      7 14 21 28
 M   1  8 15 22 29
 T   2  9 16 23 30
 W   3 10 17 24   
 T   4 11 18 25   
 F   5 12 19 26   
 S   6 13 20 27   

October
 S      5 12 19 26
 M      6 13 20 27
 T      7 14 21 28
 W   1  8 15 22 29
 T   2  9 16 23 30
 F   3 10 17 24 31
 S   4 11 18 25   

November
 S      2  9 16 23 30
 M      3 10 17 24   
 T      4 11 18 25   
 W      5 12 19 26   
 T      6 13 20 27   
 F      7 14 21 28   
 S   1  8 15 22 29   

December
 S      7 14 21 28
 M   1  8 15 22 29
 T   2  9 16 23 30
 W   3 10 17 24 31
 T   4 11 18 25   
 F   5 12 19 26   
 S   6 13 20 27   

Dựa vào lời giải bằng phương pháp sử dụng mảng ở trên thì việc in lịch theo chiều dọc trở nên dễ dàng hơn bao giờ hết, chỉ cần hoán đổi trị số cột i và j là xong:


#include <stdio.h>
#include <stdlib.h>

int isLeapYear(int y) {
    return (!(y % 4) && y % 100) || !(y % 400);
}

int getDow(int q, int m, int y) {
    if (m == 1) { m = 13; y--; }
    if (m == 2) { m = 14; y--; }
    int k = y % 100;
    int j = y / 100;
    int h = q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j;
    h = h % 7;
    return h == 0 ? 6 : h - 1;
}

int main()
{
    char months[12][10] = {
        "January", "February", "March", "April", "May", "June", 
        "July", "August", "September", "October", "November", "December"
    };
    char daysow[7][4] = {"S", "M", "T", "W", "T", "F", "S"};
    
    int y;
    printf("Input year: ");
    scanf("%d", &y);
    
    putchar('\n');
    
    for (int m = 1; m <= 12; m++, printf("\n")) {
        printf("%s\n", months[m - 1]);
        
        int top;
        switch(m) {
            case 2: top = isLeapYear(y) ? 29 : 28; break;
            case 4: case 6: case 9: case 11: top = 30; break;
            default: top = 31; break;
        }
        
        int dow = getDow(1, m, y);
        
        int days[6][7] = {0};
        int* d = days[0];
        for (int i = 0; i < dow; i++) d++;
        for (int i = 0; i < top; i++) *(d++) = i + 1;
        int col = dow + top > 35 ? 6 : 5;
        for (int j = 0; j < 7; j++, putchar('\n')) {
            printf("%2s ", daysow[j]);
            for (int i = 0; i < col; i++)
                days[i][j] == 0 ? printf("%3s", " ") : printf("%3d", days[i][j]);
        }
    }
    
    return 0;
}

Sử dụng mảng cấp phát động

Cũng tương tự như trên nhưng thay vì dùng mảng 2 chiều có kích thước cố định thì tôi dùng mảng cấp phát động với kích thước hàng thay đổi (5 hoặc 6) tuỳ theo số lượng ngày trong tháng. Thực ra cũng không cần thiết lắm nhưng tôi xem đây như một cách để luyện tập thực hành với mảng cấp phát động trong C.

Lần này tôi tách riêng phần tạo mảng thành một hàm riêng biệt initCal(dow, top) với dow (day of week) là thứ của ngày đầu tiên của tháng và top là số ngày tối đa của tháng đó. Phần khởi tạo các phần tử của mảng bạn sẽ thấy hơi phức tạp hơn so với mảng có kích thước cố định bởi vì khi tạo mảng kích thước động, tôi nhận thấy các phần tử trong mảng không phải lúc nào cũng nằm liền kề nhau. Do đó không thể sử dụng phương pháp dùng con trỏ trỏ vào phần tử đầu tiên rồi quét hết qua mảng được. Lúc này, tôi buộc phải dùng phương pháp duyệt mảng truyền thống bằng trị số dòng và cột.

In lịch theo chiều ngang:


#include <stdio.h>
#include <stdlib.h>

int isLeapYear(int y) {
    return (!(y % 4) && y % 100) || !(y % 400);
}

int getDow(int q, int m, int y) {
    if (m == 1) { m = 13; y--; }
    if (m == 2) { m = 14; y--; }
    int k = y % 100;
    int j = y / 100;
    int h = q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j;
    h = h % 7;
    return h == 0 ? 6 : h - 1;
}

int getRows(int dow, int top) {
    return dow + top > 35 ? 6 : 5;
}

int** initCal(int dow, int top) {
    int rows = getRows(dow, top);
    int cols = 7;
    int** cal = (int**) calloc(rows, sizeof(int*));
    for (int r = 0; r < rows; r++)
        cal[r] = (int*) calloc(cols, sizeof(int));
    
    int day = 1;
    for (int i = 0; i < rows; i++) {
        int* d = cal[i];
        if (i == 0) {
            for (int j = 0; j < dow; j++) d++;
            for (int j = dow; j < cols; j++) *(d++) = day++;
        } else { 
            for (int j = 0; j < cols; j++) {
                *(d++) = day++;
                if (day > top) break;
            }
        }
        if (day > top) break;
    }
    return cal;        
}

int main()
{
    char months[12][10] = {
        "January", "February", "March", "April", "May", "June", 
        "July", "August", "September", "October", "November", "December"
    };
    char daysow[7][4] = {"S", "M", "T", "W", "T", "F", "S"};
    
    int y;
    printf("Input year: ");
    scanf("%d", &y);
    
    putchar('\n');
    
    for (int m = 1; m <= 12; m++, printf("\n")) {
        printf("%s\n", months[m - 1]);
        
        int top;
        switch(m) {
            case 2: top = isLeapYear(y) ? 29 : 28; break;
            case 4: case 6: case 9: case 11: top = 30; break;
            default: top = 31; break;
        }
        
        int dow = getDow(1, m, y);
        int rows = getRows(dow, top), cols = 7;
        int** cal = initCal(dow, top);
        for (int i = 0; i < cols; i++) printf("%3s", daysow[i]);
        printf("\n");
        for (int i = 0; i < rows; i++, putchar('\n'))
            for (int j = 0; j < cols; j++)
                cal[i][j] == 0 ? printf("%3s", " ") : printf("%3d", cal[i][j]);
        free(cal);
    }
    
    return 0;
}

In lịch theo chiều dọc:


#include <stdio.h>
#include <stdlib.h>

int isLeapYear(int y) {
    return (!(y % 4) && y % 100) || !(y % 400);
}

int getDow(int q, int m, int y) {
    if (m == 1) { m = 13; y--; }
    if (m == 2) { m = 14; y--; }
    int k = y % 100;
    int j = y / 100;
    int h = q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j;
    h = h % 7;
    return h == 0 ? 6 : h - 1;
}

int getRows(int dow, int top) {
    return dow + top > 35 ? 6 : 5;
}

int** initCal(int dow, int top) {
    int rows = getRows(dow, top);
    int cols = 7;
    int** cal = (int**) calloc(rows, sizeof(int*));
    for (int r = 0; r < rows; r++)
        cal[r] = (int*) calloc(cols, sizeof(int));
    
    int day = 1;
    for (int i = 0; i < rows; i++) {
        int* d = cal[i];
        if (i == 0) {
            for (int j = 0; j < dow; j++) d++;
            for (int j = dow; j < cols; j++) *(d++) = day++;
        } else { 
            for (int j = 0; j < cols; j++) {
                *(d++) = day++;
                if (day > top) break;
            }
        }
        if (day > top) break;
    }
    return cal;        
}

int main()
{
    char months[12][10] = {
        "January", "February", "March", "April", "May", "June", 
        "July", "August", "September", "October", "November", "December"
    };
    char daysow[7][4] = {"S", "M", "T", "W", "T", "F", "S"};
    
    int y;
    printf("Input year: ");
    scanf("%d", &y);
    
    putchar('\n');
    
    for (int m = 1; m <= 12; m++, printf("\n")) {
        printf("%s\n", months[m - 1]);
        
        int top;
        switch(m) {
            case 2: top = isLeapYear(y) ? 29 : 28; break;
            case 4: case 6: case 9: case 11: top = 30; break;
            default: top = 31; break;
        }
        
        int dow = getDow(1, m, y);
        int rows = getRows(dow, top), cols = 7;
        int** cal = initCal(dow, top);
        for (int j = 0; j < cols; j++, putchar('\n')) {
            printf("%s ", daysow[j]);
            for (int i = 0; i < rows; i++)
                cal[i][j] == 0 ? printf("%3s", " ") : printf("%3d", cal[i][j]);
        }
        free(cal);
    }
    
    return 0;
}

.
Xin vui lòng chờ đợi
Dữ liệu bài viết đang được tải về

💻Nhận dạy online 1 kèm 1 Automation Test từ cơ bản tới nâng cao (From Zero to Hero) 😁😁😁
Lộ trình gồm 3 phần:
1) Kỹ thuật lập trình và tư duy lập trình cơ bản
2) Nhập môn kiểm thử (Manual Test)
3) Kiểm thử tự động (Automation Test) + Chuẩn bị cho phỏng vấn
* Lộ trình chi tiết: Xem tại đây

🎓Đối tượng người học:
- Những bạn bị mất gốc căn bản môn lập trình.
- Những bạn muốn theo con đường kiểm thử (testing), đặc biệt là kiểm thử tự động (Automation Test).

🦘Người giảng dạy:
- Mình sẽ là người trực tiếp hướng dẫn.
- Nếu là các vấn đề ngoài chuyên môn hoặc sở trường, mình sẽ nhờ các anh chị em khác cũng làm trong ngành.

🤓Giới thiệu:
- Mình đã có hơn 10 năm kinh nghiệm làm IT ở cả trong và ngoài nước. Trong đó 3 năm đầu là làm lập trình viên Java, sau đó bén duyên với mảng Automation Test và theo nghề tới tận bây giờ. Mình được đào tạo chính quy về IT từ một trường Đại học danh tiếng ở TP.HCM (hệ kỹ sư 4 năm rưỡi), có chứng chỉ ISTQB, có thể giao tiếp tốt bằng tiếng Anh và có kinh nghiệm làm việc thực tế ở cả 2 mảng Outsource và Product. Title chính thức của mình là QA Automation Engineer, tuy nhiên, mình vẫn làm những dự án cá nhân chuyên về lập trình ứng dụng như Học Tiếng Anh StreamlineSách Nhạc. Mình là người có thái độ làm việc chuyên nghiệp, chăm chỉ và luôn nhiệt tình trong công việc.

💵Chi phí và hình thức thanh toán:
- Các bạn vui lòng liên hệ qua email songtoigianvn@gmail.com (email, chat, hoặc call) để book nội dung và khung giờ học (từ 8h tối trở đi).
- Mức phí: 150.000đ/buổi, mỗi buổi 60 phút.
- Lộ trình From Zero to Hero: 4.350.000đ (29 buổi).
- Bạn có thể học riêng và đóng tiền theo từng phần nếu muốn.
- Có thể học trước 1-2 buổi trước khi quyết định đi full lộ trình hoặc từng phần.
- Thanh toán qua Momo, chuyển khoản v.v...
BÌNH LUẬN
© Copyright by CUỘC SỐNG TỐI GIẢN
Loading...